libfc: recode incoming PRLI handling

Reduce indentation in fc_rport_recv_prli_req() using gotos.
Also add payload length checks.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>

---
 drivers/scsi/libfc/fc_rport.c |  195 ++++++++++++++++++-----------------------
 1 files changed, 87 insertions(+), 108 deletions(-)


---
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 97923bb..09ec635 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -1441,136 +1441,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);
 }
 
