diff options
Diffstat (limited to 'drivers/scsi/libfc/fc_rport.c')
-rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 196 |
1 files changed, 88 insertions, 108 deletions
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 97923bb07765..39e440f0f54a 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -47,6 +47,7 @@ #include <linux/kernel.h> #include <linux/spinlock.h> #include <linux/interrupt.h> +#include <linux/slab.h> #include <linux/rcupdate.h> #include <linux/timer.h> #include <linux/workqueue.h> @@ -1441,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); } |