static void fc_lport_enter_rft_id(struct fc_lport *lport) { struct fc_frame *fp; struct fc_ns_fts *lps; int i; FC_LPORT_DBG(lport, "Entered RFT_ID state from %s state\n", fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_RFT_ID); lps = &lport->fcts; i = sizeof(lps->ff_type_map) / sizeof(lps->ff_type_map[0]); while (--i >= 0) if (ntohl(lps->ff_type_map[i]) != 0) break; if (i < 0) { fc_lport_enter_scr(lport); return; } fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + sizeof(struct fc_ns_rft)); if (!fp) { fc_lport_error(lport, fp); return; } if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RFT_ID, fc_lport_rft_id_resp, lport, lport->e_d_tov)) fc_lport_error(lport, fp); }
static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, struct fc_lport *lport) { struct fc_frame *fp; struct fc_exch *ep = fc_seq_exch(sp); unsigned int len; void *pp; void *dp; u32 f_ctl; FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n", fc_lport_state(lport)); len = fr_len(in_fp) - sizeof(struct fc_frame_header); pp = fc_frame_payload_get(in_fp, len); if (len < sizeof(__be32)) len = sizeof(__be32); fp = fc_frame_alloc(lport, len); if (fp) { dp = fc_frame_payload_get(fp, len); memcpy(dp, pp, len); *((__be32 *)dp) = htonl(ELS_LS_ACC << 24); sp = lport->tt.seq_start_next(sp); f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ; 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); } fc_frame_free(in_fp); }
/** * fc_lport_recv_echo_req() - Handle received ECHO request * @lport: The local port recieving the ECHO * @fp: ECHO request frame * * Locking Note: The lport lock is expected to be held before calling * this function. */ static void fc_lport_recv_echo_req(struct fc_lport *lport, struct fc_frame *in_fp) { struct fc_frame *fp; unsigned int len; void *pp; void *dp; FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n", fc_lport_state(lport)); len = fr_len(in_fp) - sizeof(struct fc_frame_header); pp = fc_frame_payload_get(in_fp, len); if (len < sizeof(__be32)) len = sizeof(__be32); fp = fc_frame_alloc(lport, len); if (fp) { dp = fc_frame_payload_get(fp, len); memcpy(dp, pp, len); *((__be32 *)dp) = htonl(ELS_LS_ACC << 24); fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0); lport->tt.frame_send(lport, fp); } fc_frame_free(in_fp); }
/* * Send TX_RDY (transfer ready). */ int ft_write_pending(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp; struct fcp_txrdy *txrdy; struct fc_lport *lport; struct fc_exch *ep; struct fc_frame_header *fh; u32 f_ctl; ft_dump_cmd(cmd, __func__); ep = fc_seq_exch(cmd->seq); lport = ep->lp; fp = fc_frame_alloc(lport, sizeof(*txrdy)); if (!fp) return -ENOMEM; /* Signal QUEUE_FULL */ txrdy = fc_frame_payload_get(fp, sizeof(*txrdy)); memset(txrdy, 0, sizeof(*txrdy)); txrdy->ft_burst_len = htonl(se_cmd->data_length); cmd->seq = lport->tt.seq_start_next(cmd->seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP, FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); fh = fc_frame_header_get(fp); f_ctl = ntoh24(fh->fh_f_ctl); lport->tt.seq_send(lport, cmd->seq, fp); return 0; }
/** * fc_rport_enter_flogi() - Send a FLOGI request to the fabric manager * @lport: Fibre Channel local port to be logged in to the fabric * * Locking Note: The lport lock is expected to be held before calling * this routine. */ void fc_lport_enter_flogi(struct fc_lport *lport) { struct fc_frame *fp; FC_LPORT_DBG(lport, "Entered FLOGI state from %s state\n", fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_FLOGI); if (lport->point_to_multipoint) { if (lport->port_id) fc_lport_enter_ready(lport); return; } fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); if (!fp) return fc_lport_error(lport, fp); if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, lport->vport ? ELS_FDISC : ELS_FLOGI, fc_lport_flogi_resp, lport, lport->vport ? 2 * lport->r_a_tov : lport->e_d_tov)) fc_lport_error(lport, NULL); }
static void fc_lport_recv_flogi_req(struct fc_lport *lport, struct fc_frame *rx_fp) { struct fc_frame *fp; struct fc_frame_header *fh; struct fc_els_flogi *flp; struct fc_els_flogi *new_flp; u64 remote_wwpn; u32 remote_fid; u32 local_fid; FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n", fc_lport_state(lport)); remote_fid = fc_frame_sid(rx_fp); flp = fc_frame_payload_get(rx_fp, sizeof(*flp)); if (!flp) goto out; remote_wwpn = get_unaligned_be64(&flp->fl_wwpn); if (remote_wwpn == lport->wwpn) { printk(KERN_WARNING "host%d: libfc: Received FLOGI from port " "with same WWPN %16.16llx\n", lport->host->host_no, remote_wwpn); goto out; } FC_LPORT_DBG(lport, "FLOGI from port WWPN %16.16llx\n", remote_wwpn); local_fid = FC_LOCAL_PTP_FID_LO; if (remote_wwpn < lport->wwpn) { local_fid = FC_LOCAL_PTP_FID_HI; if (!remote_fid || remote_fid == local_fid) remote_fid = FC_LOCAL_PTP_FID_LO; } else if (!remote_fid) { remote_fid = FC_LOCAL_PTP_FID_HI; } fc_lport_set_port_id(lport, local_fid, rx_fp); fp = fc_frame_alloc(lport, sizeof(*flp)); if (fp) { new_flp = fc_frame_payload_get(fp, sizeof(*flp)); fc_lport_flogi_fill(lport, new_flp, ELS_FLOGI); new_flp->fl_cmd = (u8) ELS_LS_ACC; fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); fh = fc_frame_header_get(fp); hton24(fh->fh_s_id, local_fid); hton24(fh->fh_d_id, remote_fid); lport->tt.frame_send(lport, fp); } else { fc_lport_error(lport, fp); } fc_lport_ptp_setup(lport, remote_fid, remote_wwpn, get_unaligned_be64(&flp->fl_wwnn)); out: fc_frame_free(rx_fp); }
/** * fc_lport_enter_ns() - register some object with the name server * @lport: Fibre Channel local port to register * * Locking Note: The lport lock is expected to be held before calling * this routine. */ static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state) { struct fc_frame *fp; enum fc_ns_req cmd; int size = sizeof(struct fc_ct_hdr); size_t len; FC_LPORT_DBG(lport, "Entered %s state from %s state\n", fc_lport_state_names[state], fc_lport_state(lport)); fc_lport_state_enter(lport, state); switch (state) { case LPORT_ST_RNN_ID: cmd = FC_NS_RNN_ID; size += sizeof(struct fc_ns_rn_id); break; case LPORT_ST_RSNN_NN: len = strnlen(fc_host_symbolic_name(lport->host), 255); /* if there is no symbolic name, skip to RFT_ID */ if (!len) return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID); cmd = FC_NS_RSNN_NN; size += sizeof(struct fc_ns_rsnn) + len; break; case LPORT_ST_RSPN_ID: len = strnlen(fc_host_symbolic_name(lport->host), 255); /* if there is no symbolic name, skip to RFT_ID */ if (!len) return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID); cmd = FC_NS_RSPN_ID; size += sizeof(struct fc_ns_rspn) + len; break; case LPORT_ST_RFT_ID: cmd = FC_NS_RFT_ID; size += sizeof(struct fc_ns_rft); break; case LPORT_ST_RFF_ID: cmd = FC_NS_RFF_ID; size += sizeof(struct fc_ns_rff_id); break; default: fc_lport_error(lport, NULL); return; } fp = fc_frame_alloc(lport, size); if (!fp) { fc_lport_error(lport, fp); return; } if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, cmd, fc_lport_ns_resp, lport, 3 * lport->r_a_tov)) fc_lport_error(lport, fp); }
/* * Send TX_RDY (transfer ready). */ int ft_write_pending(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp; struct fcp_txrdy *txrdy; struct fc_lport *lport; struct fc_exch *ep; struct fc_frame_header *fh; u32 f_ctl; ft_dump_cmd(cmd, __func__); if (cmd->aborted) return 0; ep = fc_seq_exch(cmd->seq); lport = ep->lp; fp = fc_frame_alloc(lport, sizeof(*txrdy)); if (!fp) return -ENOMEM; /* Signal QUEUE_FULL */ txrdy = fc_frame_payload_get(fp, sizeof(*txrdy)); memset(txrdy, 0, sizeof(*txrdy)); txrdy->ft_burst_len = htonl(se_cmd->data_length); cmd->seq = lport->tt.seq_start_next(cmd->seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP, FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); fh = fc_frame_header_get(fp); f_ctl = ntoh24(fh->fh_f_ctl); /* Only if it is 'Exchange Responder' */ if (f_ctl & FC_FC_EX_CTX) { /* Target is 'exchange responder' and sending XFER_READY * to 'exchange initiator (initiator)' */ if ((ep->xid <= lport->lro_xid) && (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) { if (se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) { /* * cmd may have been broken up into multiple * tasks. Link their sgs together so we can * operate on them all at once. */ transport_do_task_sg_chain(se_cmd); cmd->sg = se_cmd->t_tasks_sg_chained; cmd->sg_cnt = se_cmd->t_tasks_sg_chained_no; } if (cmd->sg && lport->tt.ddp_target(lport, ep->xid, cmd->sg, cmd->sg_cnt)) cmd->was_ddp_setup = 1; } } lport->tt.seq_send(lport, cmd->seq, fp); return 0; }
static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, struct fc_lport *lport) { struct fc_frame *fp; struct fc_exch *ep = fc_seq_exch(sp); struct fc_els_rnid *req; struct { struct fc_els_rnid_resp rnid; struct fc_els_rnid_cid cid; struct fc_els_rnid_gen gen; } *rp; struct fc_seq_els_data rjt_data; u8 fmt; size_t len; u32 f_ctl; FC_LPORT_DBG(lport, "Received RNID request while in state %s\n", fc_lport_state(lport)); req = fc_frame_payload_get(in_fp, sizeof(*req)); if (!req) { rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_LOGIC; rjt_data.explan = ELS_EXPL_NONE; lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); } else { fmt = req->rnid_fmt; len = sizeof(*rp); if (fmt != ELS_RNIDF_GEN || ntohl(lport->rnid_gen.rnid_atype) == 0) { fmt = ELS_RNIDF_NONE; /* nothing to provide */ len -= sizeof(rp->gen); } fp = fc_frame_alloc(lport, len); if (fp) { rp = fc_frame_payload_get(fp, len); memset(rp, 0, len); rp->rnid.rnid_cmd = ELS_LS_ACC; rp->rnid.rnid_fmt = fmt; rp->rnid.rnid_cid_len = sizeof(rp->cid); rp->cid.rnid_wwpn = htonll(lport->wwpn); rp->cid.rnid_wwnn = htonll(lport->wwnn); if (fmt == ELS_RNIDF_GEN) { rp->rnid.rnid_sid_len = sizeof(rp->gen); memcpy(&rp->gen, &lport->rnid_gen, sizeof(rp->gen)); } sp = lport->tt.seq_start_next(sp); f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ; f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT; 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); } } fc_frame_free(in_fp); }
/* * Send TX_RDY (transfer ready). */ int ft_write_pending(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp; struct fcp_txrdy *txrdy; struct fc_lport *lport; struct fc_exch *ep; struct fc_frame_header *fh; u32 f_ctl; ft_dump_cmd(cmd, __func__); ep = fc_seq_exch(cmd->seq); lport = ep->lp; fp = fc_frame_alloc(lport, sizeof(*txrdy)); if (!fp) return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES; txrdy = fc_frame_payload_get(fp, sizeof(*txrdy)); memset(txrdy, 0, sizeof(*txrdy)); txrdy->ft_burst_len = htonl(se_cmd->data_length); cmd->seq = lport->tt.seq_start_next(cmd->seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP, FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); fh = fc_frame_header_get(fp); f_ctl = ntoh24(fh->fh_f_ctl); /* Only if it is 'Exchange Responder' */ if (f_ctl & FC_FC_EX_CTX) { /* Target is 'exchange responder' and sending XFER_READY * to 'exchange initiator (initiator)' */ if ((ep->xid <= lport->lro_xid) && (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) { if (se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) { /* * Map se_mem list to scatterlist, so that * DDP can be setup. DDP setup function require * scatterlist. se_mem_list is internal to * TCM/LIO target */ transport_do_task_sg_chain(se_cmd); cmd->sg = T_TASK(se_cmd)->t_tasks_sg_chained; cmd->sg_cnt = T_TASK(se_cmd)->t_tasks_sg_chained_no; } if (cmd->sg && lport->tt.ddp_setup(lport, ep->xid, cmd->sg, cmd->sg_cnt)) cmd->was_ddp_setup = 1; } } lport->tt.seq_send(lport, cmd->seq, fp); return 0; }
/* * Send response. */ int ft_queue_status(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp; struct fcp_resp_with_ext *fcp; struct fc_lport *lport; struct fc_exch *ep; size_t len; if (cmd->aborted) return 0; ft_dump_cmd(cmd, __func__); ep = fc_seq_exch(cmd->seq); lport = ep->lp; len = sizeof(*fcp) + se_cmd->scsi_sense_length; fp = fc_frame_alloc(lport, len); if (!fp) { /* XXX shouldn't just drop it - requeue and retry? */ return 0; } fcp = fc_frame_payload_get(fp, len); memset(fcp, 0, len); fcp->resp.fr_status = se_cmd->scsi_status; len = se_cmd->scsi_sense_length; if (len) { fcp->resp.fr_flags |= FCP_SNS_LEN_VAL; fcp->ext.fr_sns_len = htonl(len); memcpy((fcp + 1), se_cmd->sense_buffer, len); } /* * Test underflow and overflow with one mask. Usually both are off. * Bidirectional commands are not handled yet. */ if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) { if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) fcp->resp.fr_flags |= FCP_RESID_OVER; else fcp->resp.fr_flags |= FCP_RESID_UNDER; fcp->ext.fr_resid = cpu_to_be32(se_cmd->residual_count); } /* * Send response. */ cmd->seq = lport->tt.seq_start_next(cmd->seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP, FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0); lport->tt.seq_send(lport, cmd->seq, fp); lport->tt.exch_done(cmd->seq); return 0; }
/** * fc_lport_recv_rnid_req() - Handle received Request Node ID data request * @lport: The local port recieving the RNID * @fp: The RNID request frame * * Locking Note: The lport lock is expected to be held before calling * this function. */ static void fc_lport_recv_rnid_req(struct fc_lport *lport, struct fc_frame *in_fp) { struct fc_frame *fp; struct fc_els_rnid *req; struct { struct fc_els_rnid_resp rnid; struct fc_els_rnid_cid cid; struct fc_els_rnid_gen gen; } *rp; struct fc_seq_els_data rjt_data; u8 fmt; size_t len; FC_LPORT_DBG(lport, "Received RNID request while in state %s\n", fc_lport_state(lport)); req = fc_frame_payload_get(in_fp, sizeof(*req)); if (!req) { rjt_data.reason = ELS_RJT_LOGIC; rjt_data.explan = ELS_EXPL_NONE; lport->tt.seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data); } else { fmt = req->rnid_fmt; len = sizeof(*rp); if (fmt != ELS_RNIDF_GEN || ntohl(lport->rnid_gen.rnid_atype) == 0) { fmt = ELS_RNIDF_NONE; /* nothing to provide */ len -= sizeof(rp->gen); } fp = fc_frame_alloc(lport, len); if (fp) { rp = fc_frame_payload_get(fp, len); memset(rp, 0, len); rp->rnid.rnid_cmd = ELS_LS_ACC; rp->rnid.rnid_fmt = fmt; rp->rnid.rnid_cid_len = sizeof(rp->cid); rp->cid.rnid_wwpn = htonll(lport->wwpn); rp->cid.rnid_wwnn = htonll(lport->wwnn); if (fmt == ELS_RNIDF_GEN) { rp->rnid.rnid_sid_len = sizeof(rp->gen); memcpy(&rp->gen, &lport->rnid_gen, sizeof(rp->gen)); } fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0); lport->tt.frame_send(lport, fp); } } fc_frame_free(in_fp); }
int ft_write_pending(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp; struct fcp_txrdy *txrdy; struct fc_lport *lport; struct fc_exch *ep; struct fc_frame_header *fh; u32 f_ctl; ft_dump_cmd(cmd, __func__); if (cmd->aborted) return 0; ep = fc_seq_exch(cmd->seq); lport = ep->lp; fp = fc_frame_alloc(lport, sizeof(*txrdy)); if (!fp) return -ENOMEM; txrdy = fc_frame_payload_get(fp, sizeof(*txrdy)); memset(txrdy, 0, sizeof(*txrdy)); txrdy->ft_burst_len = htonl(se_cmd->data_length); cmd->seq = lport->tt.seq_start_next(cmd->seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP, FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); fh = fc_frame_header_get(fp); f_ctl = ntoh24(fh->fh_f_ctl); if (f_ctl & FC_FC_EX_CTX) { if ((ep->xid <= lport->lro_xid) && (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) { if (se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) { transport_do_task_sg_chain(se_cmd); cmd->sg = se_cmd->t_tasks_sg_chained; cmd->sg_cnt = se_cmd->t_tasks_sg_chained_no; } if (cmd->sg && lport->tt.ddp_target(lport, ep->xid, cmd->sg, cmd->sg_cnt)) cmd->was_ddp_setup = 1; } } lport->tt.seq_send(lport, cmd->seq, fp); return 0; }
/** * fc_lport_ct_request() - Send CT Passthrough request * @job: The BSG Passthrough job * @lport: The local port sending the request * @did: The destination FC-ID * @tov: The timeout period to wait for the response * * Locking Note: The lport lock is expected to be held before calling * this routine. */ static int fc_lport_ct_request(struct fc_bsg_job *job, struct fc_lport *lport, u32 did, u32 tov) { struct fc_bsg_info *info; struct fc_frame *fp; struct fc_frame_header *fh; struct fc_ct_req *ct; size_t len; fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + job->request_payload.payload_len); if (!fp) return -ENOMEM; len = job->request_payload.payload_len; ct = fc_frame_payload_get(fp, len); sg_copy_to_buffer(job->request_payload.sg_list, job->request_payload.sg_cnt, ct, len); fh = fc_frame_header_get(fp); fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL; hton24(fh->fh_d_id, did); hton24(fh->fh_s_id, lport->port_id); fh->fh_type = FC_TYPE_CT; hton24(fh->fh_f_ctl, FC_FCTL_REQ); fh->fh_cs_ctl = 0; fh->fh_df_ctl = 0; fh->fh_parm_offset = 0; info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL); if (!info) { fc_frame_free(fp); return -ENOMEM; } info->job = job; info->lport = lport; info->rsp_code = FC_FS_ACC; info->nents = job->reply_payload.sg_cnt; info->sg = job->reply_payload.sg_list; if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, NULL, info, tov)) { kfree(info); return -ECOMM; } return 0; }
static int fc_lport_els_request(struct fc_bsg_job *job, struct fc_lport *lport, u32 did, u32 tov) { struct fc_bsg_info *info; struct fc_frame *fp; struct fc_frame_header *fh; char *pp; int len; fp = fc_frame_alloc(lport, job->request_payload.payload_len); if (!fp) return -ENOMEM; len = job->request_payload.payload_len; pp = fc_frame_payload_get(fp, len); sg_copy_to_buffer(job->request_payload.sg_list, job->request_payload.sg_cnt, pp, len); fh = fc_frame_header_get(fp); fh->fh_r_ctl = FC_RCTL_ELS_REQ; hton24(fh->fh_d_id, did); hton24(fh->fh_s_id, lport->port_id); fh->fh_type = FC_TYPE_ELS; hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT); fh->fh_cs_ctl = 0; fh->fh_df_ctl = 0; fh->fh_parm_offset = 0; info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL); if (!info) { fc_frame_free(fp); return -ENOMEM; } info->job = job; info->lport = lport; info->rsp_code = ELS_LS_ACC; info->nents = job->reply_payload.sg_cnt; info->sg = job->reply_payload.sg_list; if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, NULL, info, tov)) return -ECOMM; return 0; }
/* * Send TX_RDY (transfer ready). */ int ft_write_pending(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp; struct fcp_txrdy *txrdy; struct fc_lport *lport; struct fc_exch *ep; struct fc_frame_header *fh; u32 f_ctl; ft_dump_cmd(cmd, __func__); if (cmd->aborted) return 0; ep = fc_seq_exch(cmd->seq); lport = ep->lp; fp = fc_frame_alloc(lport, sizeof(*txrdy)); if (!fp) return -ENOMEM; /* Signal QUEUE_FULL */ txrdy = fc_frame_payload_get(fp, sizeof(*txrdy)); memset(txrdy, 0, sizeof(*txrdy)); txrdy->ft_burst_len = htonl(se_cmd->data_length); cmd->seq = fc_seq_start_next(cmd->seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP, FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); fh = fc_frame_header_get(fp); f_ctl = ntoh24(fh->fh_f_ctl); /* Only if it is 'Exchange Responder' */ if (f_ctl & FC_FC_EX_CTX) { /* Target is 'exchange responder' and sending XFER_READY * to 'exchange initiator (initiator)' */ if ((ep->xid <= lport->lro_xid) && (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) { if ((se_cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) && lport->tt.ddp_target(lport, ep->xid, se_cmd->t_data_sg, se_cmd->t_data_nents)) cmd->was_ddp_setup = 1; } } fc_seq_send(lport, cmd->seq, fp); return 0; }
int ft_queue_status(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp; struct fcp_resp_with_ext *fcp; struct fc_lport *lport; struct fc_exch *ep; size_t len; if (cmd->aborted) return 0; ft_dump_cmd(cmd, __func__); ep = fc_seq_exch(cmd->seq); lport = ep->lp; len = sizeof(*fcp) + se_cmd->scsi_sense_length; fp = fc_frame_alloc(lport, len); if (!fp) { return 0; } fcp = fc_frame_payload_get(fp, len); memset(fcp, 0, len); fcp->resp.fr_status = se_cmd->scsi_status; len = se_cmd->scsi_sense_length; if (len) { fcp->resp.fr_flags |= FCP_SNS_LEN_VAL; fcp->ext.fr_sns_len = htonl(len); memcpy((fcp + 1), se_cmd->sense_buffer, len); } if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) { if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) fcp->resp.fr_flags |= FCP_RESID_OVER; else fcp->resp.fr_flags |= FCP_RESID_UNDER; fcp->ext.fr_resid = cpu_to_be32(se_cmd->residual_count); } cmd->seq = lport->tt.seq_start_next(cmd->seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP, FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0); lport->tt.seq_send(lport, cmd->seq, fp); lport->tt.exch_done(cmd->seq); return 0; }
void fc_lport_enter_flogi(struct fc_lport *lport) { struct fc_frame *fp; FC_LPORT_DBG(lport, "Entered FLOGI state from %s state\n", fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_FLOGI); fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); if (!fp) return fc_lport_error(lport, fp); if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_FLOGI, fc_lport_flogi_resp, lport, lport->e_d_tov)) fc_lport_error(lport, NULL); }
static void fc_lport_enter_scr(struct fc_lport *lport) { struct fc_frame *fp; FC_LPORT_DBG(lport, "Entered SCR state from %s state\n", fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_SCR); fp = fc_frame_alloc(lport, sizeof(struct fc_els_scr)); if (!fp) { fc_lport_error(lport, fp); return; } if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR, fc_lport_scr_resp, lport, lport->e_d_tov)) fc_lport_error(lport, NULL); }
static void fc_lport_enter_logo(struct fc_lport *lport) { struct fc_frame *fp; struct fc_els_logo *logo; FC_LPORT_DBG(lport, "Entered LOGO state from %s state\n", fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_LOGO); fp = fc_frame_alloc(lport, sizeof(*logo)); if (!fp) { fc_lport_error(lport, fp); return; } if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO, fc_lport_logo_resp, lport, lport->e_d_tov)) fc_lport_error(lport, NULL); }
static void fc_lport_enter_rpn_id(struct fc_lport *lport) { struct fc_frame *fp; FC_LPORT_DBG(lport, "Entered RPN_ID state from %s state\n", fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_RPN_ID); fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + sizeof(struct fc_ns_rn_id)); if (!fp) { fc_lport_error(lport, fp); return; } if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RPN_ID, fc_lport_rpn_id_resp, lport, lport->e_d_tov)) fc_lport_error(lport, NULL); }
/* * Send a FCP response including SCSI status and optional FCP rsp_code. * status is SAM_STAT_GOOD (zero) iff code is valid. * This is used in error cases, such as allocation failures. */ static void ft_send_resp_status(struct fc_lport *lport, const struct fc_frame *rx_fp, u32 status, enum fcp_resp_rsp_codes code) { struct fc_frame *fp; struct fc_seq *sp; const struct fc_frame_header *fh; size_t len; struct fcp_resp_with_ext *fcp; struct fcp_resp_rsp_info *info; fh = fc_frame_header_get(rx_fp); pr_debug("FCP error response: did %x oxid %x status %x code %x\n", ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id), status, code); len = sizeof(*fcp); if (status == SAM_STAT_GOOD) len += sizeof(*info); fp = fc_frame_alloc(lport, len); if (!fp) return; fcp = fc_frame_payload_get(fp, len); memset(fcp, 0, len); fcp->resp.fr_status = status; if (status == SAM_STAT_GOOD) { fcp->ext.fr_rsp_len = htonl(sizeof(*info)); fcp->resp.fr_flags |= FCP_RSP_LEN_VAL; info = (struct fcp_resp_rsp_info *)(fcp + 1); info->rsp_code = code; } fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0); sp = fr_seq(fp); if (sp) { lport->tt.seq_send(lport, sp, fp); lport->tt.exch_done(sp); } else { lport->tt.frame_send(lport, fp); } }
static void qedf_rec_compl(struct qedf_els_cb_arg *cb_arg) { struct qedf_ioreq *orig_io_req; struct qedf_ioreq *rec_req; struct qedf_mp_req *mp_req; struct fc_frame_header *mp_fc_hdr, *fh; struct fc_frame *fp; void *resp_buf, *fc_payload; u32 resp_len; struct fc_lport *lport; struct qedf_ctx *qedf; int refcount; enum fc_rctl r_ctl; struct fc_els_ls_rjt *rjt; struct fc_els_rec_acc *acc; u8 opcode; u32 offset, e_stat; struct scsi_cmnd *sc_cmd; bool srr_needed = false; rec_req = cb_arg->io_req; qedf = rec_req->fcport->qedf; lport = qedf->lport; orig_io_req = cb_arg->aborted_io_req; if (!orig_io_req) goto out_free; if (rec_req->event != QEDF_IOREQ_EV_ELS_TMO && rec_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) cancel_delayed_work_sync(&orig_io_req->timeout_work); refcount = kref_read(&orig_io_req->refcount); QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p," " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n", orig_io_req, orig_io_req->xid, rec_req->xid, refcount); /* If a REC times out, free resources */ if (rec_req->event == QEDF_IOREQ_EV_ELS_TMO) goto out_put; /* Normalize response data into struct fc_frame */ mp_req = &(rec_req->mp_req); mp_fc_hdr = &(mp_req->resp_fc_hdr); resp_len = mp_req->resp_len; acc = resp_buf = mp_req->resp_buf; fp = fc_frame_alloc(lport, resp_len); if (!fp) { QEDF_ERR(&(qedf->dbg_ctx), "fc_frame_alloc failure.\n"); goto out_put; } /* Copy frame header from firmware into fp */ fh = (struct fc_frame_header *)fc_frame_header_get(fp); memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); /* Copy payload from firmware into fp */ fc_payload = fc_frame_payload_get(fp, resp_len); memcpy(fc_payload, resp_buf, resp_len); opcode = fc_frame_payload_op(fp); if (opcode == ELS_LS_RJT) { rjt = fc_frame_payload_get(fp, sizeof(*rjt)); QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Received LS_RJT for REC: er_reason=0x%x, " "er_explan=0x%x.\n", rjt->er_reason, rjt->er_explan); /* * The following response(s) mean that we need to reissue the * request on another exchange. We need to do this without * informing the upper layers lest it cause an application * error. */ if ((rjt->er_reason == ELS_RJT_LOGIC || rjt->er_reason == ELS_RJT_UNAB) && rjt->er_explan == ELS_EXPL_OXID_RXID) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Handle CMD LOST case.\n"); qedf_requeue_io_req(orig_io_req); } } else if (opcode == ELS_LS_ACC) { offset = ntohl(acc->reca_fc4value); e_stat = ntohl(acc->reca_e_stat); QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Received LS_ACC for REC: offset=0x%x, e_stat=0x%x.\n", offset, e_stat); if (e_stat & ESB_ST_SEQ_INIT) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Target has the seq init\n"); goto out_free_frame; } sc_cmd = orig_io_req->sc_cmd; if (!sc_cmd) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "sc_cmd is NULL for xid=0x%x.\n", orig_io_req->xid); goto out_free_frame; } /* SCSI write case */ if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { if (offset == orig_io_req->data_xfer_len) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "WRITE - response lost.\n"); r_ctl = FC_RCTL_DD_CMD_STATUS; srr_needed = true; offset = 0; } else { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "WRITE - XFER_RDY/DATA lost.\n"); r_ctl = FC_RCTL_DD_DATA_DESC; /* Use data from warning CQE instead of REC */ offset = orig_io_req->tx_buf_off; } /* SCSI read case */ } else { if (orig_io_req->rx_buf_off == orig_io_req->data_xfer_len) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "READ - response lost.\n"); srr_needed = true; r_ctl = FC_RCTL_DD_CMD_STATUS; offset = 0; } else { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "READ - DATA lost.\n"); /* * For read case we always set the offset to 0 * for sequence recovery task. */ offset = 0; r_ctl = FC_RCTL_DD_SOL_DATA; } } if (srr_needed) qedf_send_srr(orig_io_req, offset, r_ctl); else qedf_initiate_seq_cleanup(orig_io_req, offset, r_ctl); } out_free_frame: fc_frame_free(fp); out_put: /* Put reference for original command since REC completed */ kref_put(&orig_io_req->refcount, qedf_release_cmd); out_free: kfree(cb_arg); }
/* * Send response. */ int ft_queue_status(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp; struct fcp_resp_with_ext *fcp; struct fc_lport *lport; struct fc_exch *ep; size_t len; int rc; if (cmd->aborted) return 0; ft_dump_cmd(cmd, __func__); ep = fc_seq_exch(cmd->seq); lport = ep->lp; len = sizeof(*fcp) + se_cmd->scsi_sense_length; fp = fc_frame_alloc(lport, len); if (!fp) { se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; return -ENOMEM; } fcp = fc_frame_payload_get(fp, len); memset(fcp, 0, len); fcp->resp.fr_status = se_cmd->scsi_status; len = se_cmd->scsi_sense_length; if (len) { fcp->resp.fr_flags |= FCP_SNS_LEN_VAL; fcp->ext.fr_sns_len = htonl(len); memcpy((fcp + 1), se_cmd->sense_buffer, len); } /* * Test underflow and overflow with one mask. Usually both are off. * Bidirectional commands are not handled yet. */ if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) { if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) fcp->resp.fr_flags |= FCP_RESID_OVER; else fcp->resp.fr_flags |= FCP_RESID_UNDER; fcp->ext.fr_resid = cpu_to_be32(se_cmd->residual_count); } /* * Send response. */ cmd->seq = lport->tt.seq_start_next(cmd->seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP, FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0); rc = lport->tt.seq_send(lport, cmd->seq, fp); if (rc) { pr_info_ratelimited("%s: Failed to send response frame %p, " "xid <0x%x>\n", __func__, fp, ep->xid); /* * Generate a TASK_SET_FULL status to notify the initiator * to reduce it's queue_depth after the se_cmd response has * been re-queued by target-core. */ se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; return -ENOMEM; } lport->tt.exch_done(cmd->seq); return 0; }
static void qedf_l2_els_compl(struct qedf_els_cb_arg *cb_arg) { struct qedf_ioreq *els_req; struct qedf_rport *fcport; struct qedf_mp_req *mp_req; struct fc_frame *fp; struct fc_frame_header *fh, *mp_fc_hdr; void *resp_buf, *fc_payload; u32 resp_len; u16 l2_oxid; l2_oxid = cb_arg->l2_oxid; els_req = cb_arg->io_req; if (!els_req) { QEDF_ERR(NULL, "els_req is NULL.\n"); goto free_arg; } /* * If we are flushing the command just free the cb_arg as none of the * response data will be valid. */ if (els_req->event == QEDF_IOREQ_EV_ELS_FLUSH) goto free_arg; fcport = els_req->fcport; mp_req = &(els_req->mp_req); mp_fc_hdr = &(mp_req->resp_fc_hdr); resp_len = mp_req->resp_len; resp_buf = mp_req->resp_buf; /* * If a middle path ELS command times out, don't try to return * the command but rather do any internal cleanup and then libfc * timeout the command and clean up its internal resources. */ if (els_req->event == QEDF_IOREQ_EV_ELS_TMO) { /* * If ADISC times out, libfc will timeout the exchange and then * try to send a PLOGI which will timeout since the session is * still offloaded. Force libfc to logout the session which * will offload the connection and allow the PLOGI response to * flow over the LL2 path. */ if (cb_arg->op == ELS_ADISC) qedf_restart_rport(fcport); return; } if (sizeof(struct fc_frame_header) + resp_len > QEDF_PAGE_SIZE) { QEDF_ERR(&(fcport->qedf->dbg_ctx), "resp_len is " "beyond page size.\n"); goto free_arg; } fp = fc_frame_alloc(fcport->qedf->lport, resp_len); if (!fp) { QEDF_ERR(&(fcport->qedf->dbg_ctx), "fc_frame_alloc failure.\n"); return; } /* Copy frame header from firmware into fp */ fh = (struct fc_frame_header *)fc_frame_header_get(fp); memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); /* Copy payload from firmware into fp */ fc_payload = fc_frame_payload_get(fp, resp_len); memcpy(fc_payload, resp_buf, resp_len); QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, "Completing OX_ID 0x%x back to libfc.\n", l2_oxid); qedf_process_l2_frame_compl(fcport, fp, l2_oxid); free_arg: kfree(cb_arg); }
/* * Deliver read data back to initiator. * XXX TBD handle resource problems later. */ int ft_queue_data_in(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp = NULL; struct fc_exch *ep; struct fc_lport *lport; struct scatterlist *sg = NULL; size_t remaining; u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF; u32 mem_off = 0; u32 fh_off = 0; u32 frame_off = 0; size_t frame_len = 0; size_t mem_len = 0; size_t tlen; size_t off_in_page; struct page *page = NULL; int use_sg; int error; void *page_addr; void *from; void *to = NULL; if (cmd->aborted) return 0; if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL) goto queue_status; ep = fc_seq_exch(cmd->seq); lport = ep->lp; cmd->seq = lport->tt.seq_start_next(cmd->seq); remaining = se_cmd->data_length; /* * Setup to use first mem list entry, unless no data. */ BUG_ON(remaining && !se_cmd->t_data_sg); if (remaining) { sg = se_cmd->t_data_sg; mem_len = sg->length; mem_off = sg->offset; page = sg_page(sg); } /* no scatter/gather in skb for odd word length due to fc_seq_send() */ use_sg = !(remaining % 4); while (remaining) { struct fc_seq *seq = cmd->seq; if (!seq) { pr_debug("%s: Command aborted, xid 0x%x\n", __func__, ep->xid); break; } if (!mem_len) { sg = sg_next(sg); mem_len = min((size_t)sg->length, remaining); mem_off = sg->offset; page = sg_page(sg); } if (!frame_len) { /* * If lport's has capability of Large Send Offload LSO) * , then allow 'frame_len' to be as big as 'lso_max' * if indicated transfer length is >= lport->lso_max */ frame_len = (lport->seq_offload) ? lport->lso_max : cmd->sess->max_frame; frame_len = min(frame_len, remaining); fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len); if (!fp) return -ENOMEM; to = fc_frame_payload_get(fp, 0); fh_off = frame_off; frame_off += frame_len; /* * Setup the frame's max payload which is used by base * driver to indicate HW about max frame size, so that * HW can do fragmentation appropriately based on * "gso_max_size" of underline netdev. */ fr_max_payload(fp) = cmd->sess->max_frame; } tlen = min(mem_len, frame_len); if (use_sg) { off_in_page = mem_off; BUG_ON(!page); get_page(page); skb_fill_page_desc(fp_skb(fp), skb_shinfo(fp_skb(fp))->nr_frags, page, off_in_page, tlen); fr_len(fp) += tlen; fp_skb(fp)->data_len += tlen; fp_skb(fp)->truesize += PAGE_SIZE << compound_order(page); } else { BUG_ON(!page); from = kmap_atomic(page + (mem_off >> PAGE_SHIFT)); page_addr = from; from += mem_off & ~PAGE_MASK; tlen = min(tlen, (size_t)(PAGE_SIZE - (mem_off & ~PAGE_MASK))); memcpy(to, from, tlen); kunmap_atomic(page_addr); to += tlen; } mem_off += tlen; mem_len -= tlen; frame_len -= tlen; remaining -= tlen; if (frame_len && (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN)) continue; if (!remaining) f_ctl |= FC_FC_END_SEQ; fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, FC_TYPE_FCP, f_ctl, fh_off); error = lport->tt.seq_send(lport, seq, fp); if (error) { pr_info_ratelimited("%s: Failed to send frame %p, " "xid <0x%x>, remaining %zu, " "lso_max <0x%x>\n", __func__, fp, ep->xid, remaining, lport->lso_max); /* * Go ahead and set TASK_SET_FULL status ignoring the * rest of the DataIN, and immediately attempt to * send the response via ft_queue_status() in order * to notify the initiator that it should reduce it's * per LUN queue_depth. */ se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; break; } } queue_status: return ft_queue_status(se_cmd); }
static void qedf_srr_compl(struct qedf_els_cb_arg *cb_arg) { struct qedf_ioreq *orig_io_req; struct qedf_ioreq *srr_req; struct qedf_mp_req *mp_req; struct fc_frame_header *mp_fc_hdr, *fh; struct fc_frame *fp; void *resp_buf, *fc_payload; u32 resp_len; struct fc_lport *lport; struct qedf_ctx *qedf; int refcount; u8 opcode; srr_req = cb_arg->io_req; qedf = srr_req->fcport->qedf; lport = qedf->lport; orig_io_req = cb_arg->aborted_io_req; if (!orig_io_req) goto out_free; clear_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags); if (srr_req->event != QEDF_IOREQ_EV_ELS_TMO && srr_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) cancel_delayed_work_sync(&orig_io_req->timeout_work); refcount = kref_read(&orig_io_req->refcount); QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p," " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n", orig_io_req, orig_io_req->xid, srr_req->xid, refcount); /* If a SRR times out, simply free resources */ if (srr_req->event == QEDF_IOREQ_EV_ELS_TMO) goto out_put; /* Normalize response data into struct fc_frame */ mp_req = &(srr_req->mp_req); mp_fc_hdr = &(mp_req->resp_fc_hdr); resp_len = mp_req->resp_len; resp_buf = mp_req->resp_buf; fp = fc_frame_alloc(lport, resp_len); if (!fp) { QEDF_ERR(&(qedf->dbg_ctx), "fc_frame_alloc failure.\n"); goto out_put; } /* Copy frame header from firmware into fp */ fh = (struct fc_frame_header *)fc_frame_header_get(fp); memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); /* Copy payload from firmware into fp */ fc_payload = fc_frame_payload_get(fp, resp_len); memcpy(fc_payload, resp_buf, resp_len); opcode = fc_frame_payload_op(fp); switch (opcode) { case ELS_LS_ACC: QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "SRR success.\n"); break; case ELS_LS_RJT: QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS, "SRR rejected.\n"); qedf_initiate_abts(orig_io_req, true); break; } fc_frame_free(fp); out_put: /* Put reference for original command since SRR completed */ kref_put(&orig_io_req->refcount, qedf_release_cmd); out_free: kfree(cb_arg); }
void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg) { struct bnx2fc_cmd *orig_io_req, *new_io_req; struct bnx2fc_cmd *rec_req; struct bnx2fc_mp_req *mp_req; struct fc_frame_header *fc_hdr, *fh; struct fc_els_ls_rjt *rjt; struct fc_els_rec_acc *acc; struct bnx2fc_rport *tgt; struct fcoe_err_report_entry *err_entry; struct scsi_cmnd *sc_cmd; enum fc_rctl r_ctl; unsigned char *buf; void *resp_buf; struct fc_frame *fp; u8 opcode; u32 offset; u32 e_stat; u32 resp_len, hdr_len; int rc = 0; bool send_seq_clnp = false; bool abort_io = false; BNX2FC_MISC_DBG("Entered rec_compl callback\n"); rec_req = cb_arg->io_req; orig_io_req = cb_arg->aborted_io_req; BNX2FC_IO_DBG(rec_req, "rec_compl: orig xid = 0x%x", orig_io_req->xid); tgt = orig_io_req->tgt; /* Handle REC timeout case */ if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rec_req->req_flags)) { BNX2FC_IO_DBG(rec_req, "timed out, abort " "orig_io - 0x%x\n", orig_io_req->xid); /* els req is timed out. send abts for els */ rc = bnx2fc_initiate_abts(rec_req); if (rc != SUCCESS) { BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts " "failed. issue cleanup\n"); bnx2fc_initiate_cleanup(rec_req); } orig_io_req->rec_retry++; /* REC timedout. send ABTS to the orig IO req */ if (orig_io_req->rec_retry <= REC_RETRY_COUNT) { spin_unlock_bh(&tgt->tgt_lock); rc = bnx2fc_send_rec(orig_io_req); spin_lock_bh(&tgt->tgt_lock); if (!rc) goto rec_compl_done; } rc = bnx2fc_initiate_abts(orig_io_req); if (rc != SUCCESS) { BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts " "failed xid = 0x%x. issue cleanup\n", orig_io_req->xid); bnx2fc_initiate_cleanup(orig_io_req); } goto rec_compl_done; } if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags)) { BNX2FC_IO_DBG(rec_req, "completed" "orig_io - 0x%x\n", orig_io_req->xid); goto rec_compl_done; } if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { BNX2FC_IO_DBG(rec_req, "abts in prog " "orig_io - 0x%x\n", orig_io_req->xid); goto rec_compl_done; } mp_req = &(rec_req->mp_req); fc_hdr = &(mp_req->resp_fc_hdr); resp_len = mp_req->resp_len; acc = resp_buf = mp_req->resp_buf; hdr_len = sizeof(*fc_hdr); buf = kzalloc(PAGE_SIZE, GFP_ATOMIC); if (!buf) { printk(KERN_ERR PFX "rec buf: mem alloc failure\n"); goto rec_compl_done; } memcpy(buf, fc_hdr, hdr_len); memcpy(buf + hdr_len, resp_buf, resp_len); fp = fc_frame_alloc(NULL, resp_len); if (!fp) { printk(KERN_ERR PFX "fc_frame_alloc failure\n"); goto free_buf; } fh = (struct fc_frame_header *) fc_frame_header_get(fp); /* Copy FC Frame header and payload into the frame */ memcpy(fh, buf, hdr_len + resp_len); opcode = fc_frame_payload_op(fp); if (opcode == ELS_LS_RJT) { BNX2FC_IO_DBG(rec_req, "opcode is RJT\n"); rjt = fc_frame_payload_get(fp, sizeof(*rjt)); if ((rjt->er_reason == ELS_RJT_LOGIC || rjt->er_reason == ELS_RJT_UNAB) && rjt->er_explan == ELS_EXPL_OXID_RXID) { BNX2FC_IO_DBG(rec_req, "handle CMD LOST case\n"); new_io_req = bnx2fc_cmd_alloc(tgt); if (!new_io_req) goto abort_io; new_io_req->sc_cmd = orig_io_req->sc_cmd; /* cleanup orig_io_req that is with the FW */ set_bit(BNX2FC_FLAG_CMD_LOST, &orig_io_req->req_flags); bnx2fc_initiate_cleanup(orig_io_req); /* Post a new IO req with the same sc_cmd */ BNX2FC_IO_DBG(rec_req, "Post IO request again\n"); rc = bnx2fc_post_io_req(tgt, new_io_req); if (!rc) goto free_frame; BNX2FC_IO_DBG(rec_req, "REC: io post err\n"); } abort_io: rc = bnx2fc_initiate_abts(orig_io_req); if (rc != SUCCESS) { BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts " "failed. issue cleanup\n"); bnx2fc_initiate_cleanup(orig_io_req); } } else if (opcode == ELS_LS_ACC) { /* REVISIT: Check if the exchange is already aborted */ offset = ntohl(acc->reca_fc4value); e_stat = ntohl(acc->reca_e_stat); if (e_stat & ESB_ST_SEQ_INIT) { BNX2FC_IO_DBG(rec_req, "target has the seq init\n"); goto free_frame; } BNX2FC_IO_DBG(rec_req, "e_stat = 0x%x, offset = 0x%x\n", e_stat, offset); /* Seq initiative is with us */ err_entry = (struct fcoe_err_report_entry *) &orig_io_req->err_entry; sc_cmd = orig_io_req->sc_cmd; if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { /* SCSI WRITE command */ if (offset == orig_io_req->data_xfer_len) { BNX2FC_IO_DBG(rec_req, "WRITE - resp lost\n"); /* FCP_RSP lost */ r_ctl = FC_RCTL_DD_CMD_STATUS; offset = 0; } else { /* start transmitting from offset */ BNX2FC_IO_DBG(rec_req, "XFER_RDY/DATA lost\n"); send_seq_clnp = true; r_ctl = FC_RCTL_DD_DATA_DESC; if (bnx2fc_initiate_seq_cleanup(orig_io_req, offset, r_ctl)) abort_io = true; /* XFER_RDY */ } } else { /* SCSI READ command */ if (err_entry->data.rx_buf_off == orig_io_req->data_xfer_len) { /* FCP_RSP lost */ BNX2FC_IO_DBG(rec_req, "READ - resp lost\n"); r_ctl = FC_RCTL_DD_CMD_STATUS; offset = 0; } else { /* request retransmission from this offset */ send_seq_clnp = true; offset = err_entry->data.rx_buf_off; BNX2FC_IO_DBG(rec_req, "RD DATA lost\n"); /* FCP_DATA lost */ r_ctl = FC_RCTL_DD_SOL_DATA; if (bnx2fc_initiate_seq_cleanup(orig_io_req, offset, r_ctl)) abort_io = true; } } if (abort_io) { rc = bnx2fc_initiate_abts(orig_io_req); if (rc != SUCCESS) { BNX2FC_IO_DBG(rec_req, "rec_compl:initiate_abts" " failed. issue cleanup\n"); bnx2fc_initiate_cleanup(orig_io_req); } } else if (!send_seq_clnp) { BNX2FC_IO_DBG(rec_req, "Send SRR - FCP_RSP\n"); spin_unlock_bh(&tgt->tgt_lock); rc = bnx2fc_send_srr(orig_io_req, offset, r_ctl); spin_lock_bh(&tgt->tgt_lock); if (rc) { BNX2FC_IO_DBG(rec_req, "Unable to send SRR" " IO will abort\n"); } } } free_frame: fc_frame_free(fp); free_buf: kfree(buf); rec_compl_done: kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); kfree(cb_arg); }
/** * fc_lport_recv_flogi_req() - Receive a FLOGI request * @lport: The local port that recieved the request * @rx_fp: The FLOGI frame * * A received FLOGI request indicates a point-to-point connection. * Accept it with the common service parameters indicating our N port. * Set up to do a PLOGI if we have the higher-number WWPN. * * Locking Note: The lport lock is expected to be held before calling * this function. */ static void fc_lport_recv_flogi_req(struct fc_lport *lport, struct fc_frame *rx_fp) { struct fc_frame *fp; struct fc_frame_header *fh; struct fc_els_flogi *flp; struct fc_els_flogi *new_flp; u64 remote_wwpn; u32 remote_fid; u32 local_fid; FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n", fc_lport_state(lport)); remote_fid = fc_frame_sid(rx_fp); flp = fc_frame_payload_get(rx_fp, sizeof(*flp)); if (!flp) goto out; remote_wwpn = get_unaligned_be64(&flp->fl_wwpn); if (remote_wwpn == lport->wwpn) { printk(KERN_WARNING "host%d: libfc: Received FLOGI from port " "with same WWPN %16.16llx\n", lport->host->host_no, remote_wwpn); goto out; } FC_LPORT_DBG(lport, "FLOGI from port WWPN %16.16llx\n", remote_wwpn); /* * XXX what is the right thing to do for FIDs? * The originator might expect our S_ID to be 0xfffffe. * But if so, both of us could end up with the same FID. */ local_fid = FC_LOCAL_PTP_FID_LO; if (remote_wwpn < lport->wwpn) { local_fid = FC_LOCAL_PTP_FID_HI; if (!remote_fid || remote_fid == local_fid) remote_fid = FC_LOCAL_PTP_FID_LO; } else if (!remote_fid) { remote_fid = FC_LOCAL_PTP_FID_HI; } fc_lport_set_port_id(lport, local_fid, rx_fp); fp = fc_frame_alloc(lport, sizeof(*flp)); if (fp) { new_flp = fc_frame_payload_get(fp, sizeof(*flp)); fc_lport_flogi_fill(lport, new_flp, ELS_FLOGI); new_flp->fl_cmd = (u8) ELS_LS_ACC; /* * Send the response. If this fails, the originator should * repeat the sequence. */ fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); fh = fc_frame_header_get(fp); hton24(fh->fh_s_id, local_fid); hton24(fh->fh_d_id, remote_fid); lport->tt.frame_send(lport, fp); } else { fc_lport_error(lport, fp); } fc_lport_ptp_setup(lport, remote_fid, remote_wwpn, get_unaligned_be64(&flp->fl_wwnn)); out: fc_frame_free(rx_fp); }
void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) { struct bnx2fc_mp_req *mp_req; struct fc_frame_header *fc_hdr, *fh; struct bnx2fc_cmd *srr_req; struct bnx2fc_cmd *orig_io_req; struct fc_frame *fp; unsigned char *buf; void *resp_buf; u32 resp_len, hdr_len; u8 opcode; int rc = 0; orig_io_req = cb_arg->aborted_io_req; srr_req = cb_arg->io_req; if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &srr_req->req_flags)) { /* SRR timedout */ BNX2FC_IO_DBG(srr_req, "srr timed out, abort " "orig_io - 0x%x\n", orig_io_req->xid); rc = bnx2fc_initiate_abts(srr_req); if (rc != SUCCESS) { BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts " "failed. issue cleanup\n"); bnx2fc_initiate_cleanup(srr_req); } if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { BNX2FC_IO_DBG(srr_req, "srr_compl:xid 0x%x flags = %lx", orig_io_req->xid, orig_io_req->req_flags); goto srr_compl_done; } orig_io_req->srr_retry++; if (orig_io_req->srr_retry <= SRR_RETRY_COUNT) { struct bnx2fc_rport *tgt = orig_io_req->tgt; spin_unlock_bh(&tgt->tgt_lock); rc = bnx2fc_send_srr(orig_io_req, orig_io_req->srr_offset, orig_io_req->srr_rctl); spin_lock_bh(&tgt->tgt_lock); if (!rc) goto srr_compl_done; } rc = bnx2fc_initiate_abts(orig_io_req); if (rc != SUCCESS) { BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts " "failed xid = 0x%x. issue cleanup\n", orig_io_req->xid); bnx2fc_initiate_cleanup(orig_io_req); } goto srr_compl_done; } if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { BNX2FC_IO_DBG(srr_req, "srr_compl:xid - 0x%x flags = %lx", orig_io_req->xid, orig_io_req->req_flags); goto srr_compl_done; } mp_req = &(srr_req->mp_req); fc_hdr = &(mp_req->resp_fc_hdr); resp_len = mp_req->resp_len; resp_buf = mp_req->resp_buf; hdr_len = sizeof(*fc_hdr); buf = kzalloc(PAGE_SIZE, GFP_ATOMIC); if (!buf) { printk(KERN_ERR PFX "srr buf: mem alloc failure\n"); goto srr_compl_done; } memcpy(buf, fc_hdr, hdr_len); memcpy(buf + hdr_len, resp_buf, resp_len); fp = fc_frame_alloc(NULL, resp_len); if (!fp) { printk(KERN_ERR PFX "fc_frame_alloc failure\n"); goto free_buf; } fh = (struct fc_frame_header *) fc_frame_header_get(fp); /* Copy FC Frame header and payload into the frame */ memcpy(fh, buf, hdr_len + resp_len); opcode = fc_frame_payload_op(fp); switch (opcode) { case ELS_LS_ACC: BNX2FC_IO_DBG(srr_req, "SRR success\n"); break; case ELS_LS_RJT: BNX2FC_IO_DBG(srr_req, "SRR rejected\n"); rc = bnx2fc_initiate_abts(orig_io_req); if (rc != SUCCESS) { BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts " "failed xid = 0x%x. issue cleanup\n", orig_io_req->xid); bnx2fc_initiate_cleanup(orig_io_req); } break; default: BNX2FC_IO_DBG(srr_req, "srr compl - invalid opcode = %d\n", opcode); break; } fc_frame_free(fp); free_buf: kfree(buf); srr_compl_done: kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); }