/* * Copyright (c) 2010 Cisco Systems, Inc. * * This program is free software; you may redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include /* * See also upstream commit e31ac898ac29 ("scsi: libfc: Move scsi/fc_encode.h * to libfc"). That commit moved fc_fill_fc_hdr() from into * . */ #if defined(FC_FILL_FC_HDR_IN_SCSI_FC_ENCODE_H) #include #else #include #endif #include "fcst.h" /* * ft_set_cmd_state() - set the state of a command */ static enum ft_cmd_state ft_set_cmd_state(struct ft_cmd *fcmd, enum ft_cmd_state new) { enum ft_cmd_state previous; spin_lock(&fcmd->lock); previous = fcmd->state; if (previous != FT_STATE_DONE) fcmd->state = new; spin_unlock(&fcmd->lock); return previous; } /* * ft_test_and_set_cmd_state() - test and set the state of a command * * Returns true if and only if the previous command state was equal to 'old'. */ bool ft_test_and_set_cmd_state(struct ft_cmd *fcmd, enum ft_cmd_state old, enum ft_cmd_state new) { enum ft_cmd_state previous; WARN_ON(old == FT_STATE_DONE); WARN_ON(new == FT_STATE_NEW); spin_lock(&fcmd->lock); previous = fcmd->state; if (previous == old) fcmd->state = new; spin_unlock(&fcmd->lock); return previous == old; } static void ft_abort_cmd(struct scst_cmd *cmd) { struct ft_cmd *fcmd = scst_cmd_get_tgt_priv(cmd); struct fc_seq *sp = fcmd->seq; struct fc_exch *ep = fc_seq_exch(sp); pr_err("%s: cmd %p ox_id %#x rx_id %#x state %d\n", __func__, cmd, ep->oxid, ep->rxid, fcmd->state); spin_lock(&fcmd->lock); switch (fcmd->state) { case FT_STATE_NEW: case FT_STATE_DATA_IN: case FT_STATE_MGMT: /* * Do nothing - defer abort processing until * srpt_xmit_response() is invoked. */ break; case FT_STATE_NEED_DATA: /* SCST_DATA_WRITE */ fcmd->state = FT_STATE_DATA_IN; scst_rx_data(cmd, SCST_RX_STATUS_ERROR_FATAL, SCST_CONTEXT_THREAD); break; case FT_STATE_CMD_RSP_SENT: /* * ft_send_response() is either in progress or has finished. * Wait until the SCST core has invoked ft_cmd_done(). */ break; case FT_STATE_MGMT_RSP_SENT: default: pr_info("Unexpected command state %d\n", fcmd->state); __WARN(); fcmd->state = FT_STATE_DONE; break; } spin_unlock(&fcmd->lock); } /* * Free command and associated frame. */ static void ft_cmd_done(struct ft_cmd *fcmd) { struct fc_frame *fp = fcmd->req_frame; struct fc_seq *sp = fcmd->seq; #ifndef NEW_LIBFC_API struct fc_lport *lport = fr_dev(fp); #endif if (sp) #ifdef NEW_LIBFC_API fc_exch_done(sp); #else lport->tt.exch_done(sp); #endif if (fr_seq(fp)) #ifdef NEW_LIBFC_API fc_seq_release(fr_seq(fp)); #else lport->tt.seq_release(fr_seq(fp)); #endif fc_frame_free(fp); kfree(fcmd); } /* * Free command - callback from SCST. */ void ft_cmd_free(struct scst_cmd *cmd) { struct ft_cmd *fcmd = scst_cmd_get_tgt_priv(cmd); ft_cmd_done(fcmd); } /* * Send response. */ int ft_send_response(struct scst_cmd *cmd) { struct ft_cmd *fcmd; struct fc_frame *fp; struct fcp_resp_with_ext *fcp; struct fc_lport *lport; struct fc_exch *ep; unsigned int slen; size_t len; enum ft_cmd_state prev_state; int resid = 0; int bi_resid = 0; int error; int dir; u32 status; fcmd = scst_cmd_get_tgt_priv(cmd); ep = fc_seq_exch(fcmd->seq); lport = ep->lp; WARN_ON(fcmd->state != FT_STATE_NEW && fcmd->state != FT_STATE_DATA_IN); prev_state = ft_set_cmd_state(fcmd, FT_STATE_CMD_RSP_SENT); if (scst_cmd_aborted_on_xmit(cmd)) { FT_IO_DBG("cmd aborted did %x oxid %x\n", ep->did, ep->oxid); ft_abort_cmd(cmd); goto done; } if (!scst_cmd_get_is_send_status(cmd)) { FT_IO_DBG("send status not set. feature not implemented\n"); error = SCST_TGT_RES_FATAL_ERROR; goto err; } status = scst_cmd_get_status(cmd); dir = scst_cmd_get_data_direction(cmd); slen = scst_cmd_get_sense_buffer_len(cmd); len = sizeof(*fcp) + slen; /* * Send read data and set underflow/overflow residual count. * For bi-directional comands, the bi_resid is for the read direction. */ if (dir & SCST_DATA_WRITE) resid = (signed int)scst_cmd_get_bufflen(cmd) - fcmd->write_data_len; if (dir & SCST_DATA_READ) { error = ft_send_read_data(cmd); if (error) { FT_ERR("ft_send_read_data returned %d\n", error); goto err; } if (dir == SCST_DATA_BIDI) { bi_resid = (signed int)scst_cmd_get_out_bufflen(cmd) - scst_cmd_get_resp_data_len(cmd); if (bi_resid) len += sizeof(__be32); } else resid = (signed int)scst_cmd_get_bufflen(cmd) - scst_cmd_get_resp_data_len(cmd); } fp = fc_frame_alloc(lport, len); if (!fp) { error = SCST_TGT_RES_QUEUE_FULL; goto err; } fcp = fc_frame_payload_get(fp, len); memset(fcp, 0, sizeof(*fcp)); fcp->resp.fr_status = status; if (slen) { fcp->resp.fr_flags |= FCP_SNS_LEN_VAL; fcp->ext.fr_sns_len = htonl(slen); memcpy(fcp + 1, scst_cmd_get_sense_buffer(cmd), slen); } if (bi_resid) { if (bi_resid < 0) { fcp->resp.fr_flags |= FCP_BIDI_READ_OVER; bi_resid = -bi_resid; } else fcp->resp.fr_flags |= FCP_BIDI_READ_UNDER; *(__be32 *)((u8 *)(fcp + 1) + slen) = htonl(bi_resid); } if (resid) { if (resid < 0) { resid = -resid; fcp->resp.fr_flags |= FCP_RESID_OVER; } else fcp->resp.fr_flags |= FCP_RESID_UNDER; fcp->ext.fr_resid = htonl(resid); } FT_IO_DBG("response did %x oxid %x\n", ep->did, ep->oxid); /* * Send response. */ #ifdef NEW_LIBFC_API fcmd->seq = fc_seq_start_next(fcmd->seq); #else fcmd->seq = lport->tt.seq_start_next(fcmd->seq); #endif 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); #ifdef NEW_LIBFC_API error = FCST_INJ_SEND_ERR(fc_seq_send(lport, fcmd->seq, fp)); #else error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq, fp)); #endif if (error < 0) { pr_err("Sending response for exchange with OX_ID %#x and RX_ID %#x failed: %d\n", ep->oxid, ep->rxid, error); error = error == -ENOMEM ? SCST_TGT_RES_QUEUE_FULL : SCST_TGT_RES_FATAL_ERROR; goto err; } done: scst_tgt_cmd_done(cmd, SCST_CONTEXT_SAME); return SCST_TGT_RES_SUCCESS; err: ft_set_cmd_state(fcmd, prev_state); WARN_ONCE(error != SCST_TGT_RES_QUEUE_FULL && error != SCST_TGT_RES_FATAL_ERROR, "%s: invalid error code %d\n", __func__, error); return error; } /* * FC sequence response handler for follow-on sequences (data) and aborts. */ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg) { struct scst_cmd *cmd = arg; struct fc_frame_header *fh; /* * If an error is being reported, it must be FC_EX_CLOSED. * Timeouts don't occur on incoming requests, and there are * currently no other errors. * The PRLO handler will be also called by libfc to delete * the session and all pending commands, so we ignore this response. */ if (IS_ERR(fp)) { pr_err("exchange error %ld - aborting cmd %p / tag %lld\n", -PTR_ERR(fp), cmd, cmd->tag); ft_abort_cmd(cmd); return; } fh = fc_frame_header_get(fp); switch (fh->fh_r_ctl) { case FC_RCTL_DD_SOL_DATA: /* write data */ ft_recv_write_data(cmd, fp); break; case FC_RCTL_DD_UNSOL_CTL: /* command */ case FC_RCTL_DD_SOL_CTL: /* transfer ready */ case FC_RCTL_DD_DATA_DESC: /* transfer ready */ default: pr_info("%s: unhandled frame r_ctl %x\n", __func__, fh->fh_r_ctl); fc_frame_free(fp); break; } } /* * Command timeout. * SCST calls this when the command has taken too long in the device handler. */ void ft_cmd_timeout(struct scst_cmd *cmd) { FT_IO_DBG("%p: timeout\n", cmd); ft_abort_cmd(cmd); } /* * Send TX_RDY (transfer ready). */ int ft_send_xfer_rdy(struct scst_cmd *cmd) { struct ft_cmd *fcmd; struct fc_frame *fp; struct fcp_txrdy *txrdy; struct fc_lport *lport; struct fc_exch *ep; int error; fcmd = scst_cmd_get_tgt_priv(cmd); ep = fc_seq_exch(fcmd->seq); lport = ep->lp; fp = fc_frame_alloc(lport, sizeof(*txrdy)); if (!fp) return SCST_TGT_RES_QUEUE_FULL; WARN_ON(!ft_test_and_set_cmd_state(fcmd, FT_STATE_NEW, FT_STATE_NEED_DATA)); txrdy = fc_frame_payload_get(fp, sizeof(*txrdy)); memset(txrdy, 0, sizeof(*txrdy)); txrdy->ft_data_ro = 0; txrdy->ft_burst_len = htonl(scst_cmd_get_bufflen(cmd)); #ifdef NEW_LIBFC_API fcmd->seq = fc_seq_start_next(fcmd->seq); #else fcmd->seq = lport->tt.seq_start_next(fcmd->seq); #endif 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); #ifdef NEW_LIBFC_API error = FCST_INJ_SEND_ERR(fc_seq_send(lport, fcmd->seq, fp)); #else error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq, fp)); #endif switch (error) { case 0: return SCST_TGT_RES_SUCCESS; case -ENOMEM: ft_set_cmd_state(fcmd, FT_STATE_NEW); return SCST_TGT_RES_QUEUE_FULL; default: ft_set_cmd_state(fcmd, FT_STATE_NEW); return SCST_TGT_RES_FATAL_ERROR; } } /* * Send a FCP response including SCSI status and optional FCP rsp_code. * status is SAM_STAT_GOOD (zero) if code is valid. * This is used in error cases, such as allocation failures. */ static void ft_send_resp_status(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; struct fc_lport *lport; fh = fc_frame_header_get(rx_fp); FT_IO_DBG("FCP error response: did %x oxid %x status %x code %x\n", ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id), status, code); lport = fr_dev(rx_fp); 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) #ifdef NEW_LIBFC_API fc_seq_send(lport, sp, fp); #else lport->tt.seq_send(lport, sp, fp); #endif else lport->tt.frame_send(lport, fp); } /* * Send error or task management response. * Always frees the cmd and associated state. */ static void ft_send_resp_code(struct ft_cmd *fcmd, enum fcp_resp_rsp_codes code) { ft_send_resp_status(fcmd->req_frame, SAM_STAT_GOOD, code); ft_cmd_done(fcmd); } void ft_cmd_tm_done(struct scst_mgmt_cmd *mcmd) { struct ft_cmd *fcmd; enum fcp_resp_rsp_codes code; fcmd = scst_mgmt_cmd_get_tgt_priv(mcmd); if (!fcmd) return; ft_set_cmd_state(fcmd, FT_STATE_MGMT_RSP_SENT); switch (scst_mgmt_cmd_get_status(mcmd)) { case SCST_MGMT_STATUS_SUCCESS: code = FCP_TMF_CMPL; break; case SCST_MGMT_STATUS_REJECTED: code = FCP_TMF_REJECTED; break; case SCST_MGMT_STATUS_LUN_NOT_EXIST: code = FCP_TMF_INVALID_LUN; break; case SCST_MGMT_STATUS_TASK_NOT_EXIST: case SCST_MGMT_STATUS_FN_NOT_SUPPORTED: case SCST_MGMT_STATUS_FAILED: default: code = FCP_TMF_FAILED; break; } FT_IO_DBG("tm cmd done fn %d code %d\n", mcmd->fn, code); ft_send_resp_code(fcmd, code); } /* * Handle an incoming FCP task management command frame. * Note that this may be called directly from the softirq context. */ static void ft_recv_tm(struct scst_session *scst_sess, struct ft_cmd *fcmd, struct fcp_cmnd *fcp) { struct scst_rx_mgmt_params params; int ret; ft_set_cmd_state(fcmd, FT_STATE_MGMT); scst_rx_mgmt_params_init(¶ms); params.lun = fcp->fc_lun.scsi_lun; params.lun_len = sizeof(fcp->fc_lun); params.lun_set = 1; params.atomic = SCST_ATOMIC; params.tgt_priv = fcmd; switch (fcp->fc_tm_flags) { case FCP_TMF_LUN_RESET: params.fn = SCST_LUN_RESET; break; case FCP_TMF_TGT_RESET: params.fn = SCST_TARGET_RESET; params.lun_set = 0; break; case FCP_TMF_CLR_TASK_SET: params.fn = SCST_CLEAR_TASK_SET; break; case FCP_TMF_ABT_TASK_SET: params.fn = SCST_ABORT_TASK_SET; break; case FCP_TMF_CLR_ACA: params.fn = SCST_CLEAR_ACA; break; default: /* * FCP4r01 indicates having a combination of * tm_flags set is invalid. */ FT_IO_DBG("invalid FCP tm_flags %x\n", fcp->fc_tm_flags); ft_send_resp_code(fcmd, FCP_CMND_FIELDS_INVALID); return; } FT_IO_DBG("submit tm cmd fn %d\n", params.fn); ret = scst_rx_mgmt_fn(scst_sess, ¶ms); FT_IO_DBG("scst_rx_mgmt_fn ret %d\n", ret); if (ret) ft_send_resp_code(fcmd, FCP_TMF_FAILED); } /* * Handle an incoming FCP command frame. * Note that this may be called directly from the softirq context. */ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp) { struct fc_seq *sp; struct scst_cmd *cmd; struct ft_cmd *fcmd = NULL; struct fcp_cmnd *fcp; struct fc_lport *lport; int data_dir; u32 data_len; int cdb_len; lport = sess->tport->lport; #ifdef NEW_LIBFC_API sp = fc_seq_assign(lport, fp); #else sp = lport->tt.seq_assign(lport, fp); #endif if (!sp) goto busy; fcmd = kzalloc(sizeof(*fcmd), GFP_ATOMIC); if (!fcmd) goto busy; fcmd->max_payload = sess->max_payload; fcmd->max_lso_payload = sess->max_lso_payload; fcmd->req_frame = fp; spin_lock_init(&fcmd->lock); fcp = fc_frame_payload_get(fp, sizeof(*fcp)); if (!fcp) goto err; if (fcp->fc_tm_flags) { ft_recv_tm(sess->scst_sess, fcmd, fcp); return; } /* * re-check length including specified CDB length. * data_len is just after the CDB. */ cdb_len = fcp->fc_flags & FCP_CFL_LEN_MASK; fcp = fc_frame_payload_get(fp, sizeof(*fcp) + cdb_len); if (!fcp) goto err; cdb_len += sizeof(fcp->fc_cdb); data_len = ntohl(*(__be32 *)(fcp->fc_cdb + cdb_len)); cmd = scst_rx_cmd(sess->scst_sess, fcp->fc_lun.scsi_lun, sizeof(fcp->fc_lun), fcp->fc_cdb, cdb_len, SCST_ATOMIC); if (!cmd) goto busy; fcmd->scst_cmd = cmd; scst_cmd_set_tgt_priv(cmd, fcmd); fcmd->state = FT_STATE_NEW; fcmd->seq = sp; #ifdef NEW_LIBFC_API fc_seq_set_resp(sp, ft_recv_seq, cmd); #else lport->tt.seq_set_resp(sp, ft_recv_seq, cmd); #endif switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) { case 0: default: data_dir = SCST_DATA_NONE; break; case FCP_CFL_RDDATA: data_dir = SCST_DATA_READ; break; case FCP_CFL_WRDATA: data_dir = SCST_DATA_WRITE; break; case FCP_CFL_RDDATA | FCP_CFL_WRDATA: data_dir = SCST_DATA_BIDI; break; } scst_cmd_set_expected(cmd, data_dir, data_len); switch (fcp->fc_pri_ta & FCP_PTA_MASK) { case FCP_PTA_SIMPLE: scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_SIMPLE); break; case FCP_PTA_HEADQ: scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_HEAD_OF_QUEUE); break; case FCP_PTA_ACA: scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_ACA); break; case FCP_PTA_ORDERED: default: scst_cmd_set_queue_type(cmd, SCST_CMD_QUEUE_ORDERED); break; } scst_cmd_set_tag(cmd, fc_seq_exch(sp)->rxid); scst_cmd_init_done(cmd, SCST_CONTEXT_THREAD); return; err: ft_send_resp_code(fcmd, FCP_CMND_FIELDS_INVALID); return; busy: FT_IO_DBG("cmd allocation failure - sending BUSY\n"); ft_send_resp_status(fp, SAM_STAT_BUSY, 0); if (fcmd) ft_cmd_done(fcmd); else if (sp) #ifdef NEW_LIBFC_API fc_exch_done(sp); #else lport->tt.exch_done(sp); #endif } /* * Send FCP ELS-4 Reject. */ static void ft_cmd_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason, enum fc_els_rjt_explan explan) { struct fc_seq_els_data rjt_data; rjt_data.reason = reason; rjt_data.explan = explan; #ifdef NEW_LIBFC_API fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); #else fr_dev(rx_fp)->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); #endif } /* * Handle an incoming FCP ELS-4 command frame. * Note that this may be called directly from the softirq context. */ static void ft_recv_els4(struct ft_sess *sess, struct fc_frame *fp) { u8 op = fc_frame_payload_op(fp); switch (op) { case ELS_SRR: /* TBD */ default: FT_IO_DBG("unsupported ELS-4 op %x\n", op); ft_cmd_ls_rjt(fp, ELS_RJT_INVAL, ELS_EXPL_NONE); fc_frame_free(fp); break; } } /* * Handle an incoming FCP frame. * Note that this may be called directly from the softirq context. */ void ft_recv_req(struct ft_sess *sess, struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); switch (fh->fh_r_ctl) { case FC_RCTL_DD_UNSOL_CMD: ft_recv_cmd(sess, fp); break; case FC_RCTL_ELS4_REQ: ft_recv_els4(sess, fp); break; default: pr_info("%s: unhandled frame r_ctl %x\n", __func__, fh->fh_r_ctl); fc_frame_free(fp); break; } }