aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorJoe Eykholt <jeykholt@cisco.com>2009-08-25 17:03:26 -0400
committerJames Bottomley <James.Bottomley@suse.de>2009-09-10 13:08:00 -0400
commit3ac6f98f4113ec1c115cf9d443a9bff816e47c0b (patch)
treef5fb72bb0f73a6304bcba19e1d3f1ffbfdfd6702 /drivers/scsi
parentf657d299cf05883e23e12a69e86842da1df378ad (diff)
[SCSI] libfc: correctly handle incoming PLOGI request.
libfc receives PLOGIs from switches which are trying to discover what kind of devices are present, and from other initiators to find out if we're a target. As an initiator, some argue we don't need to handle incoming PLOGI requests, and we currently reject them from unknown remote ports, but accept them is we're in the middle of a PLOGI to the remote port. For eventual target implementations, we want to handle them always. For incoming PLOGI, don't fail if the rport_priv doesn't exist. Just create it and go become READY without going through PRLI. If PRLI occurs, then our roles will be set and we'll become READY again. Also, allow incoming PRLI in RTV state. 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')
-rw-r--r--drivers/scsi/libfc/fc_rport.c185
1 files changed, 93 insertions, 92 deletions
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index d014b285cd1a..e121ff92c8ea 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -63,7 +63,7 @@ static void fc_rport_enter_rtv(struct fc_rport_priv *);
63static void fc_rport_enter_ready(struct fc_rport_priv *); 63static void fc_rport_enter_ready(struct fc_rport_priv *);
64static void fc_rport_enter_logo(struct fc_rport_priv *); 64static void fc_rport_enter_logo(struct fc_rport_priv *);
65 65
66static void fc_rport_recv_plogi_req(struct fc_rport_priv *, 66static void fc_rport_recv_plogi_req(struct fc_lport *,
67 struct fc_seq *, struct fc_frame *); 67 struct fc_seq *, struct fc_frame *);
68static void fc_rport_recv_prli_req(struct fc_rport_priv *, 68static void fc_rport_recv_prli_req(struct fc_rport_priv *,
69 struct fc_seq *, struct fc_frame *); 69 struct fc_seq *, struct fc_frame *);
@@ -576,15 +576,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
576 csp_seq = cssp_seq; 576 csp_seq = cssp_seq;
577 rdata->max_seq = csp_seq; 577 rdata->max_seq = csp_seq;
578 rdata->maxframe_size = fc_plogi_get_maxframe(plp, lport->mfs); 578 rdata->maxframe_size = fc_plogi_get_maxframe(plp, lport->mfs);
579 579 fc_rport_enter_prli(rdata);
580 /*
581 * If the rport is one of the well known addresses
582 * we skip PRLI and RTV and go straight to READY.
583 */
584 if (rdata->ids.port_id >= FC_FID_DOM_MGR)
585 fc_rport_enter_ready(rdata);
586 else
587 fc_rport_enter_prli(rdata);
588 } else 580 } else
589 fc_rport_error_retry(rdata, fp); 581 fc_rport_error_retry(rdata, fp);
590 582
@@ -763,6 +755,15 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
763 } *pp; 755 } *pp;
764 struct fc_frame *fp; 756 struct fc_frame *fp;
765 757
758 /*
759 * If the rport is one of the well known addresses
760 * we skip PRLI and RTV and go straight to READY.
761 */
762 if (rdata->ids.port_id >= FC_FID_DOM_MGR) {
763 fc_rport_enter_ready(rdata);
764 return;
765 }
766
766 FC_RPORT_DBG(rdata, "Port entered PRLI state from %s state\n", 767 FC_RPORT_DBG(rdata, "Port entered PRLI state from %s state\n",
767 fc_rport_state(rdata)); 768 fc_rport_state(rdata));
768 769
@@ -929,6 +930,15 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
929 els_data.explan = ELS_EXPL_NONE; 930 els_data.explan = ELS_EXPL_NONE;
930 els_data.reason = ELS_RJT_NONE; 931 els_data.reason = ELS_RJT_NONE;
931 932
933 op = fc_frame_payload_op(fp);
934 switch (op) {
935 case ELS_PLOGI:
936 fc_rport_recv_plogi_req(lport, sp, fp);
937 return;
938 default:
939 break;
940 }
941
932 fh = fc_frame_header_get(fp); 942 fh = fc_frame_header_get(fp);
933 s_id = ntoh24(fh->fh_s_id); 943 s_id = ntoh24(fh->fh_s_id);
934 944
@@ -944,11 +954,7 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
944 mutex_lock(&rdata->rp_mutex); 954 mutex_lock(&rdata->rp_mutex);
945 mutex_unlock(&lport->disc.disc_mutex); 955 mutex_unlock(&lport->disc.disc_mutex);
946 956
947 op = fc_frame_payload_op(fp);
948 switch (op) { 957 switch (op) {
949 case ELS_PLOGI:
950 fc_rport_recv_plogi_req(rdata, sp, fp);
951 break;
952 case ELS_PRLI: 958 case ELS_PRLI:
953 fc_rport_recv_prli_req(rdata, sp, fp); 959 fc_rport_recv_prli_req(rdata, sp, fp);
954 break; 960 break;
@@ -977,48 +983,56 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
977 983
978/** 984/**
979 * fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request 985 * fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request
980 * @rdata: private remote port data 986 * @lport: local port
981 * @sp: current sequence in the PLOGI exchange 987 * @sp: current sequence in the PLOGI exchange
982 * @fp: PLOGI request frame 988 * @fp: PLOGI request frame
983 * 989 *
984 * Locking Note: The rport lock is exected to be held before calling 990 * Locking Note: The rport lock is held before calling this function.
985 * this function.
986 */ 991 */
987static void fc_rport_recv_plogi_req(struct fc_rport_priv *rdata, 992static void fc_rport_recv_plogi_req(struct fc_lport *lport,
988 struct fc_seq *sp, struct fc_frame *rx_fp) 993 struct fc_seq *sp, struct fc_frame *rx_fp)
989{ 994{
990 struct fc_lport *lport = rdata->local_port; 995 struct fc_disc *disc;
996 struct fc_rport_priv *rdata;
991 struct fc_frame *fp = rx_fp; 997 struct fc_frame *fp = rx_fp;
992 struct fc_exch *ep; 998 struct fc_exch *ep;
993 struct fc_frame_header *fh; 999 struct fc_frame_header *fh;
994 struct fc_els_flogi *pl; 1000 struct fc_els_flogi *pl;
995 struct fc_seq_els_data rjt_data; 1001 struct fc_seq_els_data rjt_data;
996 u32 sid; 1002 u32 sid, f_ctl;
997 u64 wwpn;
998 u64 wwnn;
999 enum fc_els_rjt_reason reject = 0;
1000 u32 f_ctl;
1001 rjt_data.fp = NULL;
1002 1003
1004 rjt_data.fp = NULL;
1003 fh = fc_frame_header_get(fp); 1005 fh = fc_frame_header_get(fp);
1006 sid = ntoh24(fh->fh_s_id);
1004 1007
1005 FC_RPORT_DBG(rdata, "Received PLOGI request while in state %s\n", 1008 FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
1006 fc_rport_state(rdata));
1007 1009
1008 sid = ntoh24(fh->fh_s_id);
1009 pl = fc_frame_payload_get(fp, sizeof(*pl)); 1010 pl = fc_frame_payload_get(fp, sizeof(*pl));
1010 if (!pl) { 1011 if (!pl) {
1011 FC_RPORT_DBG(rdata, "Received PLOGI too short\n"); 1012 FC_RPORT_ID_DBG(lport, sid, "Received PLOGI too short\n");
1012 WARN_ON(1); 1013 rjt_data.reason = ELS_RJT_PROT;
1013 /* XXX TBD: send reject? */ 1014 rjt_data.explan = ELS_EXPL_INV_LEN;
1014 fc_frame_free(fp); 1015 goto reject;
1015 return; 1016 }
1017
1018 disc = &lport->disc;
1019 mutex_lock(&disc->disc_mutex);
1020 rdata = lport->tt.rport_create(lport, sid);
1021 if (!rdata) {
1022 mutex_unlock(&disc->disc_mutex);
1023 rjt_data.reason = ELS_RJT_UNAB;
1024 rjt_data.explan = ELS_EXPL_INSUF_RES;
1025 goto reject;
1016 } 1026 }
1017 wwpn = get_unaligned_be64(&pl->fl_wwpn); 1027
1018 wwnn = get_unaligned_be64(&pl->fl_wwnn); 1028 mutex_lock(&rdata->rp_mutex);
1029 mutex_unlock(&disc->disc_mutex);
1030
1031 rdata->ids.port_name = get_unaligned_be64(&pl->fl_wwpn);
1032 rdata->ids.node_name = get_unaligned_be64(&pl->fl_wwnn);
1019 1033
1020 /* 1034 /*
1021 * If the session was just created, possibly due to the incoming PLOGI, 1035 * If the rport was just created, possibly due to the incoming PLOGI,
1022 * set the state appropriately and accept the PLOGI. 1036 * set the state appropriately and accept the PLOGI.
1023 * 1037 *
1024 * If we had also sent a PLOGI, and if the received PLOGI is from a 1038 * If we had also sent a PLOGI, and if the received PLOGI is from a
@@ -1030,72 +1044,58 @@ static void fc_rport_recv_plogi_req(struct fc_rport_priv *rdata,
1030 */ 1044 */
1031 switch (rdata->rp_state) { 1045 switch (rdata->rp_state) {
1032 case RPORT_ST_INIT: 1046 case RPORT_ST_INIT:
1033 FC_RPORT_DBG(rdata, "Received PLOGI, wwpn %llx state INIT " 1047 FC_RPORT_DBG(rdata, "Received PLOGI in INIT state\n");
1034 "- reject\n", (unsigned long long)wwpn);
1035 reject = ELS_RJT_UNSUP;
1036 break; 1048 break;
1037 case RPORT_ST_PLOGI: 1049 case RPORT_ST_PLOGI:
1038 FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state %d\n", 1050 FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
1039 rdata->rp_state); 1051 if (rdata->ids.port_name < lport->wwpn) {
1040 if (wwpn < lport->wwpn) 1052 mutex_unlock(&rdata->rp_mutex);
1041 reject = ELS_RJT_INPROG; 1053 rjt_data.reason = ELS_RJT_INPROG;
1054 rjt_data.explan = ELS_EXPL_NONE;
1055 goto reject;
1056 }
1042 break; 1057 break;
1043 case RPORT_ST_PRLI: 1058 case RPORT_ST_PRLI:
1044 case RPORT_ST_READY: 1059 case RPORT_ST_READY:
1045 FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d "
1046 "- ignored for now\n", rdata->rp_state);
1047 /* XXX TBD - should reset */
1048 break; 1060 break;
1049 case RPORT_ST_DELETE: 1061 case RPORT_ST_DELETE:
1050 default: 1062 default:
1051 FC_RPORT_DBG(rdata, "Received PLOGI in unexpected " 1063 FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n",
1052 "state %d\n", rdata->rp_state); 1064 rdata->rp_state);
1053 fc_frame_free(fp); 1065 fc_frame_free(rx_fp);
1054 return; 1066 goto out;
1055 break;
1056 } 1067 }
1057 1068
1058 if (reject) { 1069 /*
1059 rjt_data.reason = reject; 1070 * Get session payload size from incoming PLOGI.
1060 rjt_data.explan = ELS_EXPL_NONE; 1071 */
1061 lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); 1072 rdata->maxframe_size = fc_plogi_get_maxframe(pl, lport->mfs);
1062 fc_frame_free(fp); 1073 fc_frame_free(rx_fp);
1063 } else { 1074
1064 fp = fc_frame_alloc(lport, sizeof(*pl)); 1075 /*
1065 if (fp == NULL) { 1076 * Send LS_ACC. If this fails, the originator should retry.
1066 fp = rx_fp; 1077 */
1067 rjt_data.reason = ELS_RJT_UNAB; 1078 sp = lport->tt.seq_start_next(sp);
1068 rjt_data.explan = ELS_EXPL_NONE; 1079 if (!sp)
1069 lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); 1080 goto out;
1070 fc_frame_free(fp); 1081 fp = fc_frame_alloc(lport, sizeof(*pl));
1071 } else { 1082 if (!fp)
1072 sp = lport->tt.seq_start_next(sp); 1083 goto out;
1073 WARN_ON(!sp); 1084
1074 rdata->ids.port_name = wwpn; 1085 fc_plogi_fill(lport, fp, ELS_LS_ACC);
1075 rdata->ids.node_name = wwnn; 1086 f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
1076 1087 ep = fc_seq_exch(sp);
1077 /* 1088 fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
1078 * Get session payload size from incoming PLOGI. 1089 FC_TYPE_ELS, f_ctl, 0);
1079 */ 1090 lport->tt.seq_send(lport, sp, fp);
1080 rdata->maxframe_size = 1091 fc_rport_enter_prli(rdata);
1081 fc_plogi_get_maxframe(pl, lport->mfs); 1092out:
1082 fc_frame_free(rx_fp); 1093 mutex_unlock(&rdata->rp_mutex);
1083 fc_plogi_fill(lport, fp, ELS_LS_ACC); 1094 return;
1084 1095
1085 /* 1096reject:
1086 * Send LS_ACC. If this fails, 1097 lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
1087 * the originator should retry. 1098 fc_frame_free(fp);
1088 */
1089 f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
1090 f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
1091 ep = fc_seq_exch(sp);
1092 fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
1093 FC_TYPE_ELS, f_ctl, 0);
1094 lport->tt.seq_send(lport, sp, fp);
1095 if (rdata->rp_state == RPORT_ST_PLOGI)
1096 fc_rport_enter_prli(rdata);
1097 }
1098 }
1099} 1099}
1100 1100
1101/** 1101/**
@@ -1138,6 +1138,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
1138 1138
1139 switch (rdata->rp_state) { 1139 switch (rdata->rp_state) {
1140 case RPORT_ST_PRLI: 1140 case RPORT_ST_PRLI:
1141 case RPORT_ST_RTV:
1141 case RPORT_ST_READY: 1142 case RPORT_ST_READY:
1142 reason = ELS_RJT_NONE; 1143 reason = ELS_RJT_NONE;
1143 break; 1144 break;