diff options
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 137 |
1 files changed, 88 insertions, 49 deletions
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index e121ff92c8e..04e9846ad1b 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -69,7 +69,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *, struct fc_seq *, struct fc_frame *); static void fc_rport_recv_prlo_req(struct fc_rport_priv *, struct fc_seq *, struct fc_frame *); -static void fc_rport_recv_logo_req(struct fc_rport_priv *, +static void fc_rport_recv_logo_req(struct fc_lport *, struct fc_seq *, struct fc_frame *); static void fc_rport_timeout(struct work_struct *); static void fc_rport_error(struct fc_rport_priv *, struct fc_frame *); @@ -908,62 +908,56 @@ static void fc_rport_enter_logo(struct fc_rport_priv *rdata) kref_get(&rdata->kref); } - /** - * fc_rport_recv_req() - Receive a request from a rport + * fc_rport_recv_els_req() - handle a validated ELS request. + * @lport: Fibre Channel local port * @sp: current sequence in the PLOGI exchange * @fp: response frame - * @lport: Fibre Channel local port + * + * Handle incoming ELS requests that require port login. + * The ELS opcode has already been validated by the caller. * * Locking Note: Called with the lport lock held. */ -void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lport) +static void fc_rport_recv_els_req(struct fc_lport *lport, + struct fc_seq *sp, struct fc_frame *fp) { struct fc_rport_priv *rdata; struct fc_frame_header *fh; struct fc_seq_els_data els_data; - u32 s_id; - u8 op; els_data.fp = NULL; - els_data.explan = ELS_EXPL_NONE; - els_data.reason = ELS_RJT_NONE; - - op = fc_frame_payload_op(fp); - switch (op) { - case ELS_PLOGI: - fc_rport_recv_plogi_req(lport, sp, fp); - return; - default: - break; - } + els_data.reason = ELS_RJT_UNAB; + els_data.explan = ELS_EXPL_PLOGI_REQD; fh = fc_frame_header_get(fp); - s_id = ntoh24(fh->fh_s_id); mutex_lock(&lport->disc.disc_mutex); - rdata = lport->tt.rport_lookup(lport, s_id); + rdata = lport->tt.rport_lookup(lport, ntoh24(fh->fh_s_id)); if (!rdata) { mutex_unlock(&lport->disc.disc_mutex); - els_data.reason = ELS_RJT_UNAB; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); - fc_frame_free(fp); - return; + goto reject; } mutex_lock(&rdata->rp_mutex); mutex_unlock(&lport->disc.disc_mutex); - switch (op) { + switch (rdata->rp_state) { + case RPORT_ST_PRLI: + case RPORT_ST_RTV: + case RPORT_ST_READY: + break; + default: + mutex_unlock(&rdata->rp_mutex); + goto reject; + } + + switch (fc_frame_payload_op(fp)) { case ELS_PRLI: fc_rport_recv_prli_req(rdata, sp, fp); break; case ELS_PRLO: fc_rport_recv_prlo_req(rdata, sp, fp); break; - case ELS_LOGO: - fc_rport_recv_logo_req(rdata, sp, fp); - break; case ELS_RRQ: els_data.fp = fp; lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data); @@ -973,12 +967,58 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data); break; default: - els_data.reason = ELS_RJT_UNSUP; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); + fc_frame_free(fp); /* can't happen */ break; } mutex_unlock(&rdata->rp_mutex); + return; + +reject: + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); + fc_frame_free(fp); +} + +/** + * fc_rport_recv_req() - Handle a received ELS request from a rport + * @sp: current sequence in the PLOGI exchange + * @fp: response frame + * @lport: Fibre Channel local port + * + * Locking Note: Called with the lport lock held. + */ +void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, + struct fc_lport *lport) +{ + struct fc_seq_els_data els_data; + + /* + * Handle PLOGI and LOGO requests separately, since they + * don't require prior login. + * Check for unsupported opcodes first and reject them. + * For some ops, it would be incorrect to reject with "PLOGI required". + */ + switch (fc_frame_payload_op(fp)) { + case ELS_PLOGI: + fc_rport_recv_plogi_req(lport, sp, fp); + break; + case ELS_LOGO: + fc_rport_recv_logo_req(lport, sp, fp); + break; + case ELS_PRLI: + case ELS_PRLO: + case ELS_RRQ: + case ELS_REC: + fc_rport_recv_els_req(lport, sp, fp); + break; + default: + fc_frame_free(fp); + els_data.fp = NULL; + els_data.reason = ELS_RJT_UNSUP; + els_data.explan = ELS_EXPL_NONE; + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); + break; + } } /** @@ -1276,11 +1316,6 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n", fc_rport_state(rdata)); - if (rdata->rp_state == RPORT_ST_DELETE) { - fc_frame_free(fp); - return; - } - rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_UNAB; rjt_data.explan = ELS_EXPL_NONE; @@ -1290,32 +1325,36 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, /** * fc_rport_recv_logo_req() - Handle incoming Logout (LOGO) request - * @rdata: private remote port data + * @lport: local port. * @sp: current sequence in the LOGO exchange * @fp: LOGO request frame * * Locking Note: The rport lock is exected to be held before calling * this function. */ -static void fc_rport_recv_logo_req(struct fc_rport_priv *rdata, +static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_seq *sp, struct fc_frame *fp) { struct fc_frame_header *fh; - struct fc_lport *lport = rdata->local_port; + struct fc_rport_priv *rdata; + u32 sid; fh = fc_frame_header_get(fp); + sid = ntoh24(fh->fh_s_id); - FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n", - fc_rport_state(rdata)); - - if (rdata->rp_state == RPORT_ST_DELETE) { - fc_frame_free(fp); - return; - } - - fc_rport_enter_delete(rdata, RPORT_EV_LOGO); - + mutex_lock(&lport->disc.disc_mutex); + rdata = lport->tt.rport_lookup(lport, sid); + if (rdata) { + mutex_lock(&rdata->rp_mutex); + FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n", + fc_rport_state(rdata)); + fc_rport_enter_delete(rdata, RPORT_EV_LOGO); + mutex_unlock(&rdata->rp_mutex); + } else + FC_RPORT_ID_DBG(lport, sid, + "Received LOGO from non-logged-in port\n"); + mutex_unlock(&lport->disc.disc_mutex); lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); fc_frame_free(fp); } |