Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs
[cascardo/linux.git] / drivers / scsi / libfc / fc_rport.c
index b37d0ff..39e440f 100644 (file)
@@ -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);
 }