|
@@ -1442,136 +1442,115 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
|
|
|
struct fc_els_spp *spp; /* response spp */
|
|
|
unsigned int len;
|
|
|
unsigned int plen;
|
|
|
- enum fc_els_rjt_reason reason = ELS_RJT_UNAB;
|
|
|
- enum fc_els_rjt_explan explan = ELS_EXPL_NONE;
|
|
|
enum fc_els_spp_resp resp;
|
|
|
struct fc_seq_els_data rjt_data;
|
|
|
u32 f_ctl;
|
|
|
u32 fcp_parm;
|
|
|
u32 roles = FC_RPORT_ROLE_UNKNOWN;
|
|
|
- rjt_data.fp = NULL;
|
|
|
|
|
|
+ rjt_data.fp = NULL;
|
|
|
fh = fc_frame_header_get(rx_fp);
|
|
|
|
|
|
FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
|
|
|
fc_rport_state(rdata));
|
|
|
|
|
|
- switch (rdata->rp_state) {
|
|
|
- case RPORT_ST_PRLI:
|
|
|
- case RPORT_ST_RTV:
|
|
|
- case RPORT_ST_READY:
|
|
|
- case RPORT_ST_ADISC:
|
|
|
- reason = ELS_RJT_NONE;
|
|
|
- break;
|
|
|
- default:
|
|
|
- fc_frame_free(rx_fp);
|
|
|
- return;
|
|
|
- break;
|
|
|
- }
|
|
|
len = fr_len(rx_fp) - sizeof(*fh);
|
|
|
pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
|
|
|
- if (pp == NULL) {
|
|
|
- reason = ELS_RJT_PROT;
|
|
|
- explan = ELS_EXPL_INV_LEN;
|
|
|
- } else {
|
|
|
- plen = ntohs(pp->prli.prli_len);
|
|
|
- if ((plen % 4) != 0 || plen > len) {
|
|
|
- reason = ELS_RJT_PROT;
|
|
|
- explan = ELS_EXPL_INV_LEN;
|
|
|
- } else if (plen < len) {
|
|
|
- len = plen;
|
|
|
- }
|
|
|
- plen = pp->prli.prli_spp_len;
|
|
|
- if ((plen % 4) != 0 || plen < sizeof(*spp) ||
|
|
|
- plen > len || len < sizeof(*pp)) {
|
|
|
- reason = ELS_RJT_PROT;
|
|
|
- explan = ELS_EXPL_INV_LEN;
|
|
|
- }
|
|
|
- rspp = &pp->spp;
|
|
|
+ if (!pp)
|
|
|
+ goto reject_len;
|
|
|
+ plen = ntohs(pp->prli.prli_len);
|
|
|
+ if ((plen % 4) != 0 || plen > len || plen < 16)
|
|
|
+ goto reject_len;
|
|
|
+ if (plen < len)
|
|
|
+ len = plen;
|
|
|
+ plen = pp->prli.prli_spp_len;
|
|
|
+ if ((plen % 4) != 0 || plen < sizeof(*spp) ||
|
|
|
+ plen > len || len < sizeof(*pp) || plen < 12)
|
|
|
+ goto reject_len;
|
|
|
+ rspp = &pp->spp;
|
|
|
+
|
|
|
+ fp = fc_frame_alloc(lport, len);
|
|
|
+ if (!fp) {
|
|
|
+ rjt_data.reason = ELS_RJT_UNAB;
|
|
|
+ rjt_data.explan = ELS_EXPL_INSUF_RES;
|
|
|
+ goto reject;
|
|
|
}
|
|
|
- if (reason != ELS_RJT_NONE ||
|
|
|
- (fp = fc_frame_alloc(lport, len)) == NULL) {
|
|
|
- rjt_data.reason = reason;
|
|
|
- rjt_data.explan = explan;
|
|
|
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
|
|
|
- } else {
|
|
|
- sp = lport->tt.seq_start_next(sp);
|
|
|
- WARN_ON(!sp);
|
|
|
- pp = fc_frame_payload_get(fp, len);
|
|
|
- WARN_ON(!pp);
|
|
|
- memset(pp, 0, len);
|
|
|
- pp->prli.prli_cmd = ELS_LS_ACC;
|
|
|
- pp->prli.prli_spp_len = plen;
|
|
|
- pp->prli.prli_len = htons(len);
|
|
|
- len -= sizeof(struct fc_els_prli);
|
|
|
-
|
|
|
- /* reinitialize remote port roles */
|
|
|
- rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
|
|
|
-
|
|
|
- /*
|
|
|
- * Go through all the service parameter pages and build
|
|
|
- * response. If plen indicates longer SPP than standard,
|
|
|
- * use that. The entire response has been pre-cleared above.
|
|
|
- */
|
|
|
- spp = &pp->spp;
|
|
|
- while (len >= plen) {
|
|
|
- spp->spp_type = rspp->spp_type;
|
|
|
- spp->spp_type_ext = rspp->spp_type_ext;
|
|
|
- spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
|
|
|
- resp = FC_SPP_RESP_ACK;
|
|
|
- if (rspp->spp_flags & FC_SPP_RPA_VAL)
|
|
|
- resp = FC_SPP_RESP_NO_PA;
|
|
|
- switch (rspp->spp_type) {
|
|
|
- case 0: /* common to all FC-4 types */
|
|
|
- break;
|
|
|
- case FC_TYPE_FCP:
|
|
|
- fcp_parm = ntohl(rspp->spp_params);
|
|
|
- if (fcp_parm & FCP_SPPF_RETRY)
|
|
|
- rdata->flags |= FC_RP_FLAGS_RETRY;
|
|
|
- rdata->supported_classes = FC_COS_CLASS3;
|
|
|
- if (fcp_parm & FCP_SPPF_INIT_FCN)
|
|
|
- roles |= FC_RPORT_ROLE_FCP_INITIATOR;
|
|
|
- if (fcp_parm & FCP_SPPF_TARG_FCN)
|
|
|
- roles |= FC_RPORT_ROLE_FCP_TARGET;
|
|
|
- rdata->ids.roles = roles;
|
|
|
-
|
|
|
- spp->spp_params =
|
|
|
- htonl(lport->service_params);
|
|
|
- break;
|
|
|
- default:
|
|
|
- resp = FC_SPP_RESP_INVL;
|
|
|
- break;
|
|
|
- }
|
|
|
- spp->spp_flags |= resp;
|
|
|
- len -= plen;
|
|
|
- rspp = (struct fc_els_spp *)((char *)rspp + plen);
|
|
|
- spp = (struct fc_els_spp *)((char *)spp + plen);
|
|
|
- }
|
|
|
+ sp = lport->tt.seq_start_next(sp);
|
|
|
+ WARN_ON(!sp);
|
|
|
+ pp = fc_frame_payload_get(fp, len);
|
|
|
+ WARN_ON(!pp);
|
|
|
+ memset(pp, 0, len);
|
|
|
+ pp->prli.prli_cmd = ELS_LS_ACC;
|
|
|
+ pp->prli.prli_spp_len = plen;
|
|
|
+ pp->prli.prli_len = htons(len);
|
|
|
+ len -= sizeof(struct fc_els_prli);
|
|
|
|
|
|
- /*
|
|
|
- * Send LS_ACC. If this fails, the originator should retry.
|
|
|
- */
|
|
|
- f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
|
|
|
- f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
|
|
|
- ep = fc_seq_exch(sp);
|
|
|
- fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
|
|
|
- FC_TYPE_ELS, f_ctl, 0);
|
|
|
- lport->tt.seq_send(lport, sp, fp);
|
|
|
+ /* reinitialize remote port roles */
|
|
|
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
|
|
|
|
|
|
- /*
|
|
|
- * Get lock and re-check state.
|
|
|
- */
|
|
|
- switch (rdata->rp_state) {
|
|
|
- case RPORT_ST_PRLI:
|
|
|
- fc_rport_enter_ready(rdata);
|
|
|
+ /*
|
|
|
+ * Go through all the service parameter pages and build
|
|
|
+ * response. If plen indicates longer SPP than standard,
|
|
|
+ * use that. The entire response has been pre-cleared above.
|
|
|
+ */
|
|
|
+ spp = &pp->spp;
|
|
|
+ while (len >= plen) {
|
|
|
+ spp->spp_type = rspp->spp_type;
|
|
|
+ spp->spp_type_ext = rspp->spp_type_ext;
|
|
|
+ spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
|
|
|
+ resp = FC_SPP_RESP_ACK;
|
|
|
+
|
|
|
+ switch (rspp->spp_type) {
|
|
|
+ case 0: /* common to all FC-4 types */
|
|
|
break;
|
|
|
- case RPORT_ST_READY:
|
|
|
- case RPORT_ST_ADISC:
|
|
|
+ case FC_TYPE_FCP:
|
|
|
+ fcp_parm = ntohl(rspp->spp_params);
|
|
|
+ if (fcp_parm & FCP_SPPF_RETRY)
|
|
|
+ rdata->flags |= FC_RP_FLAGS_RETRY;
|
|
|
+ rdata->supported_classes = FC_COS_CLASS3;
|
|
|
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
|
|
|
+ roles |= FC_RPORT_ROLE_FCP_INITIATOR;
|
|
|
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
|
|
|
+ roles |= FC_RPORT_ROLE_FCP_TARGET;
|
|
|
+ rdata->ids.roles = roles;
|
|
|
+
|
|
|
+ spp->spp_params = htonl(lport->service_params);
|
|
|
break;
|
|
|
default:
|
|
|
+ resp = FC_SPP_RESP_INVL;
|
|
|
break;
|
|
|
}
|
|
|
+ spp->spp_flags |= resp;
|
|
|
+ len -= plen;
|
|
|
+ rspp = (struct fc_els_spp *)((char *)rspp + plen);
|
|
|
+ spp = (struct fc_els_spp *)((char *)spp + plen);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Send LS_ACC. If this fails, the originator should retry.
|
|
|
+ */
|
|
|
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
|
|
|
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
|
|
|
+ ep = fc_seq_exch(sp);
|
|
|
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
|
|
|
+ FC_TYPE_ELS, f_ctl, 0);
|
|
|
+ lport->tt.seq_send(lport, sp, fp);
|
|
|
+
|
|
|
+ switch (rdata->rp_state) {
|
|
|
+ case RPORT_ST_PRLI:
|
|
|
+ fc_rport_enter_ready(rdata);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
}
|
|
|
+ goto drop;
|
|
|
+
|
|
|
+reject_len:
|
|
|
+ rjt_data.reason = ELS_RJT_PROT;
|
|
|
+ rjt_data.explan = ELS_EXPL_INV_LEN;
|
|
|
+reject:
|
|
|
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
|
|
|
+drop:
|
|
|
fc_frame_free(rx_fp);
|
|
|
}
|
|
|
|