From: Anil Gurumurthy <anil.gurumurthy@xxxxxxxxxx> This patch Adds following code in the driver to support FC-NVMe Target - Updated ql2xenablenvme to allow FC-NVMe Target operation - Added Link Serviccce Request handling for NVMe Target - Added passthru IOCB for LS4 request - Added CTIO for sending response to FW - Added FC4 Registration for FC-NVMe Target - Added PUREX IOCB support for login processing in FC-NVMe Target mode - Added Continuation IOCB for PUREX - Added Session creation with PUREX IOCB in FC-NVMe Target mode Signed-off-by: Anil Gurumurthy <anil.gurumurthy@xxxxxxxxxx> Signed-off-by: Giridhar Malavali <giridhar.malavali@xxxxxxxxxx> Signed-off-by: Darren Trapp <darren.trapp@xxxxxxxxxx> Signed-off-by: Himanshu Madhani <himanshu.madhani@xxxxxxxxxx> --- drivers/scsi/qla2xxx/qla_def.h | 35 +- drivers/scsi/qla2xxx/qla_fw.h | 263 ++++++++++ drivers/scsi/qla2xxx/qla_gbl.h | 17 +- drivers/scsi/qla2xxx/qla_gs.c | 14 +- drivers/scsi/qla2xxx/qla_init.c | 46 +- drivers/scsi/qla2xxx/qla_isr.c | 112 ++++- drivers/scsi/qla2xxx/qla_mbx.c | 101 +++- drivers/scsi/qla2xxx/qla_nvme.h | 33 -- drivers/scsi/qla2xxx/qla_os.c | 77 ++- drivers/scsi/qla2xxx/qla_target.c | 977 +++++++++++++++++++++++++++++++++++++- drivers/scsi/qla2xxx/qla_target.h | 90 ++++ 11 files changed, 1697 insertions(+), 68 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 26b93c563f92..feda0b90f62e 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -480,6 +480,10 @@ struct srb_iocb { uint32_t dl; uint32_t timeout_sec; struct list_head entry; + uint32_t exchange_address; + uint16_t nport_handle; + uint8_t vp_index; + void *cmd; } nvme; struct { u16 cmd; @@ -490,7 +494,11 @@ struct srb_iocb { struct timer_list timer; void (*timeout)(void *); }; - +struct srb_nvme_els_rsp { + dma_addr_t dma_addr; + void *dma_ptr; + void *ptr; +}; /* Values for srb_ctx type */ #define SRB_LOGIN_CMD 1 #define SRB_LOGOUT_CMD 2 @@ -515,6 +523,11 @@ struct srb_iocb { #define SRB_PRLI_CMD 21 #define SRB_CTRL_VP 22 #define SRB_PRLO_CMD 23 +#define SRB_NVME_ELS_RSP 24 +#define SRB_NVMET_LS 25 +#define SRB_NVMET_FCP 26 +#define SRB_NVMET_ABTS 27 +#define SRB_NVMET_SEND_ABTS 28 enum { TYPE_SRB, @@ -545,10 +558,13 @@ typedef struct srb { int rc; int retry_count; struct completion comp; + struct work_struct nvmet_comp_work; + uint16_t comp_status; union { struct srb_iocb iocb_cmd; struct bsg_job *bsg_job; struct srb_cmd scmd; + struct srb_nvme_els_rsp snvme_els; } u; void (*done)(void *, int); void (*free)(void *); @@ -2273,6 +2289,15 @@ struct qlt_plogi_ack_t { void *fcport; }; +/* NVMET */ +struct qlt_purex_plogi_ack_t { + struct list_head list; + struct __fc_plogi rcvd_plogi; + port_id_t id; + int ref_count; + void *fcport; +}; + struct ct_sns_desc { struct ct_sns_pkt *ct_sns; dma_addr_t ct_sns_dma; @@ -3235,6 +3260,7 @@ enum qla_work_type { QLA_EVT_SP_RETRY, QLA_EVT_IIDMA, QLA_EVT_ELS_PLOGI, + QLA_EVT_NEW_NVMET_SESS, }; @@ -4229,6 +4255,7 @@ typedef struct scsi_qla_host { uint32_t qpairs_req_created:1; uint32_t qpairs_rsp_created:1; uint32_t nvme_enabled:1; + uint32_t nvmet_enabled:1; } flags; atomic_t loop_state; @@ -4274,6 +4301,7 @@ typedef struct scsi_qla_host { #define N2N_LOGIN_NEEDED 30 #define IOCB_WORK_ACTIVE 31 #define SET_ZIO_THRESHOLD_NEEDED 32 +#define NVMET_PUREX 33 unsigned long pci_flags; #define PFLG_DISCONNECTED 0 /* PCI device removed */ @@ -4314,6 +4342,7 @@ typedef struct scsi_qla_host { uint8_t fabric_node_name[WWN_SIZE]; struct nvme_fc_local_port *nvme_local_port; + struct nvmet_fc_target_port *targetport; struct completion nvme_del_done; struct list_head nvme_rport_list; @@ -4394,6 +4423,9 @@ typedef struct scsi_qla_host { uint16_t n2n_id; struct list_head gpnid_list; struct fab_scan scan; + /*NVMET*/ + struct list_head purex_atio_list; + struct completion purex_plogi_sess; } scsi_qla_host_t; struct qla27xx_image_status { @@ -4664,6 +4696,7 @@ struct sff_8247_a0 { !ha->current_topology) #include "qla_target.h" +#include "qla_nvmet.h" #include "qla_gbl.h" #include "qla_dbg.h" #include "qla_inline.h" diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 50c1e6c62e31..67a42d153f64 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -723,6 +723,269 @@ struct ct_entry_24xx { uint32_t dseg_1_len; /* Data segment 1 length. */ }; +/* NVME-T changes */ +/* + * Fibre Channel Header + * Little Endian format. As received in PUREX and PURLS + */ +struct __fc_hdr { + uint16_t did_lo; + uint8_t did_hi; + uint8_t r_ctl; + uint16_t sid_lo; + uint8_t sid_hi; + uint8_t cs_ctl; + uint16_t f_ctl_lo; + uint8_t f_ctl_hi; + uint8_t type; + uint16_t seq_cnt; + uint8_t df_ctl; + uint8_t seq_id; + uint16_t rx_id; + uint16_t ox_id; + uint32_t param; +}; + +/* + * Fibre Channel LOGO acc + * In big endian format + */ +struct __fc_logo_acc { + uint8_t op_code; + uint8_t reserved[3]; +}; + +struct __fc_lsrjt { + uint8_t op_code; + uint8_t reserved[3]; + uint8_t reserved2; + uint8_t reason; + uint8_t exp; + uint8_t vendor; +}; + +/* + * Fibre Channel LOGO Frame + * Little Endian format. As received in PUREX + */ +struct __fc_logo { + struct __fc_hdr hdr; + uint16_t reserved; + uint8_t reserved1; + uint8_t op_code; + uint16_t sid_lo; + uint8_t sid_hi; + uint8_t reserved2; + uint8_t pname[8]; +}; + +/* + * Fibre Channel PRLI Frame + * Little Endian format. As received in PUREX + */ +struct __fc_prli { + struct __fc_hdr hdr; + uint16_t pyld_length; /* word 0 of prli */ + uint8_t page_length; + uint8_t op_code; + uint16_t common;/* word 1. 1st word of SP page */ + uint8_t type_ext; + uint8_t prli_type; +#define PRLI_TYPE_FCP 0x8 +#define PRLI_TYPE_NVME 0x28 + union { + struct { + uint32_t reserved[2]; + uint32_t sp_info; + } fcp; + struct { + uint32_t reserved[2]; + uint32_t sp_info; +#define NVME_PRLI_DISC BIT_3 +#define NVME_PRLI_TRGT BIT_4 +#define NVME_PRLI_INIT BIT_5 +#define NVME_PRLI_CONFIRMATION BIT_7 + uint32_t reserved1; + } nvme; + }; +}; + +/* + * Fibre Channel PLOGI Frame + * Little Endian format. As received in PUREX + */ +struct __fc_plogi { + uint16_t did_lo; + uint8_t did_hi; + uint8_t r_ctl; + uint16_t sid_lo; + uint8_t sid_hi; + uint8_t cs_ctl; + uint16_t f_ctl_lo; + uint8_t f_ctl_hi; + uint8_t type; + uint16_t seq_cnt; + uint8_t df_ctl; + uint8_t seq_id; + uint16_t rx_id; + uint16_t ox_id; + uint32_t param; + uint8_t rsvd[3]; + uint8_t op_code; + uint32_t cs_params[4]; /* common service params */ + uint8_t pname[8]; /* port name */ + uint8_t nname[8]; /* node name */ + uint32_t class1[4]; /* class 1 service params */ + uint32_t class2[4]; /* class 2 service params */ + uint32_t class3[4]; /* class 3 service params */ + uint32_t class4[4]; + uint32_t vndr_vers[4]; +}; + +#define IOCB_TYPE_ELS_PASSTHRU 0x53 + +/* ELS Pass-Through IOCB (IOCB_TYPE_ELS_PASSTHRU = 0x53) + */ +struct __els_pt { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; + uint16_t status; /* when returned from fw */ + uint16_t nphdl; + uint16_t tx_dsd_cnt; + uint8_t vp_index; + uint8_t sof; /* bits 7:4 */ + uint32_t rcv_exchg_id; + uint16_t rx_dsd_cnt; + uint8_t op_code; + uint8_t rsvd1; + uint16_t did_lo; + uint8_t did_hi; + uint8_t sid_hi; + uint16_t sid_lo; + uint16_t cntl_flags; +#define ELS_PT_RESPONDER_ACC (1 << 13) + uint32_t rx_bc; + uint32_t tx_bc; + uint32_t tx_dsd[2]; /* Data segment 0 address. */ + uint32_t tx_dsd_len; /* Data segment 0 length. */ + uint32_t rx_dsd[2]; /* Data segment 1 address. */ + uint32_t rx_dsd_len; /* Data segment 1 length. */ +}; + +/* + * Reject a FCP PRLI + * + */ +struct __fc_prli_rjt { + uint8_t op_code; /* word 0 of prli rjt */ + uint8_t rsvd1[3]; + uint8_t rsvd2; /* word 1 of prli rjt */ + uint8_t reason; +#define PRLI_RJT_REASON 0x3 /* logical error */ + uint8_t expl; + uint8_t vendor; +#define PRLI_RJT_FCP_RESP_LEN 8 +}; + +/* + * Fibre Channel PRLI ACC + * Payload only + */ +struct __fc_prli_acc { +/* payload only. In big-endian format */ + uint8_t op_code; /* word 0 of prli acc */ + uint8_t page_length; +#define PRLI_FCP_PAGE_LENGTH 16 +#define PRLI_NVME_PAGE_LENGTH 20 + uint16_t pyld_length; + uint8_t type; /* word 1 of prli acc */ + uint8_t type_ext; + uint16_t common; +#define PRLI_EST_FCP_PAIR 0x2000 +#define PRLI_REQ_EXEC 0x0100 +#define PRLI_REQ_DOES_NOT_EXIST 0x0400 + union { + struct { + uint32_t reserved[2]; + uint32_t sp_info; + /* hard coding resp. target, rdxfr disabled.*/ +#define FCP_PRLI_SP 0x12 + } fcp; + struct { + uint32_t reserved[2]; + uint32_t sp_info; + uint16_t reserved2; + uint16_t first_burst; + } nvme; + }; +#define PRLI_ACC_FCP_RESP_LEN 20 +#define PRLI_ACC_NVME_RESP_LEN 24 + +}; + +/* + * ISP queue - PUREX IOCB entry structure definition + */ +#define PUREX_IOCB_TYPE 0x51 /* CT Pass Through IOCB entry */ +struct purex_entry_24xx { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint16_t reserved1; + uint8_t vp_idx; + uint8_t reserved2; + + uint16_t status_flags; + uint16_t nport_handle; + + uint16_t frame_size; + uint16_t trunc_frame_size; + + uint32_t rx_xchg_addr; + + uint8_t d_id[3]; + uint8_t r_ctl; + + uint8_t s_id[3]; + uint8_t cs_ctl; + + uint8_t f_ctl[3]; + uint8_t type; + + uint16_t seq_cnt; + uint8_t df_ctl; + uint8_t seq_id; + + uint16_t rx_id; + uint16_t ox_id; + uint32_t param; + + uint8_t pyld[20]; +#define PUREX_PYLD_SIZE 44 /* Number of bytes (hdr+pyld) in this IOCB */ +}; + +#define PUREX_ENTRY_SIZE (sizeof(purex_entry_24xx_t)) + +#define CONT_SENSE_DATA 60 +/* + * Continuation Status Type 0 (IOCB_TYPE_STATUS_CONT = 0x10) + * Section 5.6 FW Interface Spec + */ +struct __status_cont { + uint8_t entry_type; /* Entry type. - 0x10 */ + uint8_t entry_count; /* Entry count. */ + uint8_t entry_status; /* Entry Status. */ + uint8_t reserved; + + uint8_t data[CONT_SENSE_DATA]; +} __packed; + + /* * ISP queue - ELS Pass-Through entry structure definition. */ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 3673fcdb033a..531f7b049caa 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -313,7 +313,10 @@ extern int qla2x00_set_fw_options(scsi_qla_host_t *, uint16_t *); extern int -qla2x00_mbx_reg_test(scsi_qla_host_t *); +qla2x00_set_purex_mode(scsi_qla_host_t *vha); + +extern int +qla2x00_mbx_reg_test(scsi_qla_host_t *vha); extern int qla2x00_verify_checksum(scsi_qla_host_t *, uint32_t); @@ -899,4 +902,16 @@ void qlt_remove_target_resources(struct qla_hw_data *); void qlt_clr_qp_table(struct scsi_qla_host *vha); void qlt_set_mode(struct scsi_qla_host *); +extern int qla2x00_get_plogi_template(scsi_qla_host_t *vha, dma_addr_t buf, + uint16_t length); +extern void qlt_dequeue_purex(struct scsi_qla_host *vha); +int qla24xx_post_nvmet_newsess_work(struct scsi_qla_host *vha, port_id_t *id, + u8 *port_name, void *pla); +int qlt_send_els_resp(srb_t *sp, struct __els_pt *pkt); +extern void nvmet_release_sessions(struct scsi_qla_host *vha); +struct fc_port *qla_nvmet_find_sess_by_s_id(scsi_qla_host_t *vha, + const uint32_t s_id); +void qla_nvme_cmpl_io(struct srb_iocb *); +void qla24xx_nvmet_abts_resp_iocb(struct scsi_qla_host *vha, + struct abts_resp_to_24xx *pkt, struct req_que *req); #endif /* _QLA_GBL_H */ diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index a24b0c2a2f00..55dc11d91b35 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -646,9 +646,11 @@ static int qla_async_rftid(scsi_qla_host_t *vha, port_id_t *d_id) ct_req->req.rft_id.port_id[0] = vha->d_id.b.domain; ct_req->req.rft_id.port_id[1] = vha->d_id.b.area; ct_req->req.rft_id.port_id[2] = vha->d_id.b.al_pa; - ct_req->req.rft_id.fc4_types[2] = 0x01; /* FCP-3 */ - if (vha->flags.nvme_enabled) + if (!vha->flags.nvmet_enabled) + ct_req->req.rft_id.fc4_types[2] = 0x01; /* FCP-3 */ + + if (vha->flags.nvme_enabled || vha->flags.nvmet_enabled) ct_req->req.rft_id.fc4_types[6] = 1; /* NVMe type 28h */ sp->u.iocb_cmd.u.ctarg.req_size = RFT_ID_REQ_SIZE; @@ -691,6 +693,10 @@ qla2x00_rff_id(scsi_qla_host_t *vha, u8 type) return (QLA_SUCCESS); } + /* only single mode for now */ + if ((vha->flags.nvmet_enabled) && (type == FC4_TYPE_FCP_SCSI)) + return (QLA_SUCCESS); + return qla_async_rffid(vha, &vha->d_id, qlt_rff_id(vha), FC4_TYPE_FCP_SCSI); } @@ -2355,7 +2361,7 @@ qla2x00_fdmiv2_rpa(scsi_qla_host_t *vha) eiter->a.fc4_types[2], eiter->a.fc4_types[1]); - if (vha->flags.nvme_enabled) { + if (vha->flags.nvme_enabled || vha->flags.nvmet_enabled) { eiter->a.fc4_types[6] = 1; /* NVMe type 28h */ ql_dbg(ql_dbg_disc, vha, 0x211f, "NVME FC4 Type = %02x 0x0 0x0 0x0 0x0 0x0.\n", @@ -2559,7 +2565,7 @@ qla2x00_fdmiv2_rpa(scsi_qla_host_t *vha) "Port Active FC4 Type = %02x %02x.\n", eiter->a.port_fc4_type[2], eiter->a.port_fc4_type[1]); - if (vha->flags.nvme_enabled) { + if (vha->flags.nvme_enabled || vha->flags.nvmet_enabled) { eiter->a.port_fc4_type[4] = 0; eiter->a.port_fc4_type[5] = 0; eiter->a.port_fc4_type[6] = 1; /* NVMe type 28h */ diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 41e5358d3739..841541201671 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -19,6 +19,7 @@ #include <target/target_core_base.h> #include "qla_target.h" +#include "qla_nvmet.h" /* * QLogic ISP2x00 Hardware Support Function Prototypes. @@ -1094,6 +1095,23 @@ int qla24xx_post_gpdb_work(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt) return qla2x00_post_work(vha, e); } +/* NVMET */ +int qla24xx_post_nvmet_newsess_work(struct scsi_qla_host *vha, port_id_t *id, + u8 *port_name, void *pla) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_NEW_NVMET_SESS); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.new_sess.id = *id; + e->u.new_sess.pla = pla; + memcpy(e->u.new_sess.port_name, port_name, WWN_SIZE); + + return qla2x00_post_work(vha, e); +} + int qla24xx_async_gpdb(struct scsi_qla_host *vha, fc_port_t *fcport, u8 opt) { srb_t *sp; @@ -3591,6 +3609,13 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) rval = qla2x00_get_fw_version(vha); if (rval != QLA_SUCCESS) goto failed; + + if (vha->flags.nvmet_enabled) { + ql_log(ql_log_info, vha, 0xffff, + "Enabling PUREX mode\n"); + qla2x00_set_purex_mode(vha); + } + ha->flags.npiv_supported = 0; if (IS_QLA2XXX_MIDTYPE(ha) && (ha->fw_attributes & BIT_2)) { @@ -3811,11 +3836,14 @@ qla24xx_update_fw_options(scsi_qla_host_t *vha) /* Move PUREX, ABTS RX & RIDA to ATIOQ */ if (ql2xmvasynctoatio && (IS_QLA83XX(ha) || IS_QLA27XX(ha))) { - if (qla_tgt_mode_enabled(vha) || - qla_dual_mode_enabled(vha)) + if ((qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)) && + qlt_op_target_mode) { + ql_log(ql_log_info, vha, 0xffff, + "Moving Purex to ATIO Q\n"); ha->fw_options[2] |= BIT_11; - else + } else { ha->fw_options[2] &= ~BIT_11; + } } if (IS_QLA25XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha)) { @@ -5463,7 +5491,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) &vha->dpc_flags)) break; } - if (vha->flags.nvme_enabled) { + if (vha->flags.nvme_enabled || + vha->flags.nvmet_enabled) { if (qla2x00_rff_id(vha, FC_TYPE_NVME)) { ql_dbg(ql_dbg_disc, vha, 0x2049, "Register NVME FC Type Features failed.\n"); @@ -5631,7 +5660,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha) new_fcport->nvme_flag = 0; new_fcport->fc4f_nvme = 0; - if (vha->flags.nvme_enabled && + if ((vha->flags.nvme_enabled || + vha->flags.nvmet_enabled) && swl[swl_idx].fc4f_nvme) { new_fcport->fc4f_nvme = swl[swl_idx].fc4f_nvme; @@ -8457,6 +8487,12 @@ qla81xx_update_fw_options(scsi_qla_host_t *vha) ha->fw_options[2] |= BIT_11; else ha->fw_options[2] &= ~BIT_11; + + if (ql2xnvmeenable == 2 && qlt_op_target_mode) { + /* Enabled PUREX node */ + ha->fw_options[1] |= FO1_ENABLE_PUREX; + ha->fw_options[2] |= BIT_11; + } } if (qla_tgt_mode_enabled(vha) || diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index d73b04e40590..5c1833f030a4 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -23,6 +23,8 @@ static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *); static int qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *, sts_entry_t *); +extern struct workqueue_struct *qla_nvmet_comp_wq; + /** * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200. * @irq: @@ -1583,6 +1585,12 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, sp->name); sp->done(sp, res); return; + case SRB_NVME_ELS_RSP: + type = "nvme els"; + ql_log(ql_log_info, vha, 0xffff, + "Completing %s: (%p) type=%d.\n", type, sp, sp->type); + sp->done(sp, 0); + return; default: ql_dbg(ql_dbg_user, vha, 0x503e, "Unrecognized SRB: (%p) type=%d.\n", sp, sp->type); @@ -2456,6 +2464,13 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) return; } + if (sp->type == SRB_NVMET_LS) { + ql_log(ql_log_info, vha, 0xffff, + "Dump NVME-LS response pkt\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075, + (uint8_t *)pkt, 64); + } + if (unlikely((state_flags & BIT_1) && (sp->type == SRB_BIDI_CMD))) { qla25xx_process_bidir_status_iocb(vha, pkt, req, handle); return; @@ -2825,6 +2840,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt) "iocb type %xh with error status %xh, handle %xh, rspq id %d\n", pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id); + ql_log(ql_log_info, vha, 0xffff, + "(%s-%d)Dumping the NVMET-ERROR pkt IOCB\n", + __func__, __LINE__); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075, + (uint8_t *)pkt, 64); + if (que >= ha->max_req_queues || !ha->req_q_map[que]) goto fatal; @@ -2918,6 +2939,23 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, sp->done(sp, 0); } +/* + * Post a completion to the NVMET layer + */ + +static void qla_nvmet_comp_work(struct work_struct *work) +{ + srb_t *sp = container_of(work, srb_t, nvmet_comp_work); + + sp->done(sp, sp->comp_status); +} + +/** + * qla24xx_nvme_ls4_iocb() - Process LS4 completions + * @vha: SCSI driver HA context + * @pkt: LS4 req packet + * @req: Request Queue + */ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, struct pt_ls4_request *pkt, struct req_que *req) { @@ -2929,11 +2967,78 @@ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, if (!sp) return; + ql_log(ql_log_info, vha, 0xc01f, + "Dumping response pkt for SRB type: %#x\n", sp->type); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075, + (uint8_t *)pkt, 16); + comp_status = le16_to_cpu(pkt->status); - sp->done(sp, comp_status); + sp->comp_status = comp_status; + /* Queue the work item */ + INIT_WORK(&sp->nvmet_comp_work, qla_nvmet_comp_work); + queue_work(qla_nvmet_comp_wq, &sp->nvmet_comp_work); } /** + * qla24xx_nvmet_fcp_iocb() - Process FCP completions + * @vha: SCSI driver HA context + * @pkt: FCP completion from firmware + * @req: Request Queue + */ +static void qla24xx_nvmet_fcp_iocb(struct scsi_qla_host *vha, + struct ctio_nvme_from_27xx *pkt, struct req_que *req) +{ + srb_t *sp; + const char func[] = "NVMET_FCP_IOCB"; + uint16_t comp_status; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; + + if ((pkt->entry_status) || (pkt->status != 1)) { + ql_log(ql_log_info, vha, 0xc01f, + "Dumping response pkt for SRB type: %#x\n", sp->type); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075, + (uint8_t *)pkt, 16); + } + + comp_status = le16_to_cpu(pkt->status); + sp->comp_status = comp_status; + /* Queue the work item */ + INIT_WORK(&sp->nvmet_comp_work, qla_nvmet_comp_work); + queue_work(qla_nvmet_comp_wq, &sp->nvmet_comp_work); +} + +/** + * qla24xx_nvmet_abts_resp_iocb() - Process ABTS completions + * @vha: SCSI driver HA context + * @pkt: ABTS completion from firmware + * @req: Request Queue + */ +void qla24xx_nvmet_abts_resp_iocb(struct scsi_qla_host *vha, + struct abts_resp_to_24xx *pkt, struct req_que *req) +{ + srb_t *sp; + const char func[] = "NVMET_ABTS_RESP_IOCB"; + uint16_t comp_status; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; + + ql_log(ql_log_info, vha, 0xc01f, + "Dumping response pkt for SRB type: %#x\n", sp->type); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075, + (uint8_t *)pkt, 16); + + comp_status = le16_to_cpu(pkt->entry_status); + sp->comp_status = comp_status; + /* Queue the work item */ + INIT_WORK(&sp->nvmet_comp_work, qla_nvmet_comp_work); + queue_work(qla_nvmet_comp_wq, &sp->nvmet_comp_work); +} +/** * qla24xx_process_response_queue() - Process response queue entries. * @vha: SCSI driver HA context * @rsp: response queue @@ -3011,6 +3116,11 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, qla24xx_nvme_ls4_iocb(vha, (struct pt_ls4_request *)pkt, rsp->req); break; + case CTIO_NVME: + qla24xx_nvmet_fcp_iocb(vha, + (struct ctio_nvme_from_27xx *)pkt, + rsp->req); + break; case NOTIFY_ACK_TYPE: if (pkt->handle == QLA_TGT_SKIP_HANDLE) qlt_response_pkt_all_vps(vha, rsp, diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index e016ee9c6d8e..0269566acae2 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -61,6 +61,7 @@ static struct rom_cmd { { MBC_READ_SFP }, { MBC_GET_RNID_PARAMS }, { MBC_GET_SET_ZIO_THRESHOLD }, + { MBC_SET_RNID_PARAMS }, }; static int is_rom_cmd(uint16_t cmd) @@ -1109,12 +1110,15 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) * FW supports nvme and driver load parameter requested nvme. * BIT 26 of fw_attributes indicates NVMe support. */ - if ((ha->fw_attributes_h & 0x400) && ql2xnvmeenable) { + if ((ha->fw_attributes_h & 0x400) && (ql2xnvmeenable == 1)) { vha->flags.nvme_enabled = 1; ql_log(ql_log_info, vha, 0xd302, "%s: FC-NVMe is Enabled (0x%x)\n", __func__, ha->fw_attributes_h); } + + if ((ha->fw_attributes_h & 0x400) && (ql2xnvmeenable == 2)) + vha->flags.nvmet_enabled = 1; } if (IS_QLA27XX(ha)) { @@ -1189,6 +1193,101 @@ qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts) return rval; } +#define OPCODE_PLOGI_TMPLT 7 +int +qla2x00_get_plogi_template(scsi_qla_host_t *vha, dma_addr_t buf, + uint16_t length) +{ + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + int rval; + + mcp->mb[0] = MBC_GET_RNID_PARAMS; + mcp->mb[1] = OPCODE_PLOGI_TMPLT << 8; + mcp->mb[2] = MSW(LSD(buf)); + mcp->mb[3] = LSW(LSD(buf)); + mcp->mb[6] = MSW(MSD(buf)); + mcp->mb[7] = LSW(MSD(buf)); + mcp->mb[8] = length; + mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->buf_size = length; + mcp->flags = MBX_DMA_IN; + mcp->tov = MBX_TOV_SECONDS; + rval = qla2x00_mailbox_command(vha, mcp); + + ql_dbg(ql_dbg_mbx, vha, 0x118f, + "%s: %s rval=%x mb[0]=%x,%x.\n", __func__, + (rval == QLA_SUCCESS) ? "Success" : "Failed", + rval, mcp->mb[0], mcp->mb[1]); + + return rval; +} + +#define OPCODE_LIST_LENGTH 32 /* ELS opcode list */ +#define OPCODE_ELS_CMD 5 /* MBx1 cmd param */ +/* + * qla2x00_set_purex_mode + * Enable purex mode for ELS commands + * + * Input: + * vha = adapter block pointer. + * + * Returns: + * qla2x00 local function return status code. + * + * Context: + * Kernel context. + */ +int +qla2x00_set_purex_mode(scsi_qla_host_t *vha) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + uint8_t *els_cmd_map; + dma_addr_t els_cmd_map_dma; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1197, + "Entered %s.\n", __func__); + + els_cmd_map = dma_zalloc_coherent(&ha->pdev->dev, OPCODE_LIST_LENGTH, + &els_cmd_map_dma, GFP_KERNEL); + if (!els_cmd_map) { + ql_log(ql_log_warn, vha, 0x7101, + "Failed to allocate RDP els command param.\n"); + return QLA_MEMORY_ALLOC_FAILED; + } + + els_cmd_map[0] = 0x28; /* enable PLOGI and LOGO ELS */ + els_cmd_map[4] = 0x13; /* enable PRLI ELS */ + els_cmd_map[10] = 0x5; + + mcp->mb[0] = MBC_SET_RNID_PARAMS; + mcp->mb[1] = OPCODE_ELS_CMD << 8; + mcp->mb[2] = MSW(LSD(els_cmd_map_dma)); + mcp->mb[3] = LSW(LSD(els_cmd_map_dma)); + mcp->mb[6] = MSW(MSD(els_cmd_map_dma)); + mcp->mb[7] = LSW(MSD(els_cmd_map_dma)); + mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = MBX_DMA_OUT; + mcp->buf_size = OPCODE_LIST_LENGTH; + rval = qla2x00_mailbox_command(vha, mcp); + + ql_dbg(ql_dbg_mbx, vha, 0x118d, + "%s: %s rval=%x mb[0]=%x,%x.\n", __func__, + (rval == QLA_SUCCESS) ? "Success" : "Failed", + rval, mcp->mb[0], mcp->mb[1]); + + dma_free_coherent(&ha->pdev->dev, OPCODE_LIST_LENGTH, + els_cmd_map, els_cmd_map_dma); + + return rval; +} + /* * qla2x00_set_fw_options diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h index 4941d107fb1c..0902c3a27adc 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.h +++ b/drivers/scsi/qla2xxx/qla_nvme.h @@ -106,39 +106,6 @@ struct pt_ls4_request { uint32_t dseg1_address[2]; uint32_t dseg1_len; }; - -#define PT_LS4_UNSOL 0x56 /* pass-up unsolicited rec FC-NVMe request */ -struct pt_ls4_rx_unsol { - uint8_t entry_type; - uint8_t entry_count; - uint16_t rsvd0; - uint16_t rsvd1; - uint8_t vp_index; - uint8_t rsvd2; - uint16_t rsvd3; - uint16_t nport_handle; - uint16_t frame_size; - uint16_t rsvd4; - uint32_t exchange_address; - uint8_t d_id[3]; - uint8_t r_ctl; - uint8_t s_id[3]; - uint8_t cs_ctl; - uint8_t f_ctl[3]; - uint8_t type; - uint16_t seq_cnt; - uint8_t df_ctl; - uint8_t seq_id; - uint16_t rx_id; - uint16_t ox_id; - uint32_t param; - uint32_t desc0; -#define PT_LS4_PAYLOAD_OFFSET 0x2c -#define PT_LS4_FIRST_PACKET_LEN 20 - uint32_t desc_len; - uint32_t payload[3]; -}; - /* * Global functions prototype in qla_nvme.c source file. */ diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index d21dd7700d5d..d10ef1577197 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -137,13 +137,17 @@ MODULE_PARM_DESC(ql2xenabledif, #if (IS_ENABLED(CONFIG_NVME_FC)) int ql2xnvmeenable = 1; +#elif (IS_ENABLED(CONFIG_NVME_TARGET_FC)) +int ql2xnvmeenable = 2; #else int ql2xnvmeenable; #endif module_param(ql2xnvmeenable, int, 0644); MODULE_PARM_DESC(ql2xnvmeenable, - "Enables NVME support. " - "0 - no NVMe. Default is Y"); + "Enables NVME support.\n" + "0 - no NVMe.\n" + "1 - initiator,\n" + "2 - target. Default is 1\n"); int ql2xenablehba_err_chk = 2; module_param(ql2xenablehba_err_chk, int, S_IRUGO|S_IWUSR); @@ -3421,6 +3425,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) qlt_add_target(ha, base_vha); + if (ql2xnvmeenable == 2) + qla_nvmet_create_targetport(base_vha); + clear_bit(PFLG_DRIVER_PROBING, &base_vha->pci_flags); if (test_bit(UNLOADING, &base_vha->dpc_flags)) @@ -3701,6 +3708,8 @@ qla2x00_remove_one(struct pci_dev *pdev) qla_nvme_delete(base_vha); + qla_nvmet_delete(base_vha); + dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size, base_vha->gnl.l, base_vha->gnl.ldma); @@ -5024,6 +5033,53 @@ static void qla_sp_retry(struct scsi_qla_host *vha, struct qla_work_evt *e) qla24xx_sp_unmap(vha, sp); } } +/* NVMET */ +static +void qla24xx_create_new_nvmet_sess(struct scsi_qla_host *vha, + struct qla_work_evt *e) +{ + unsigned long flags; + fc_port_t *fcport = NULL; + + spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); + fcport = qla2x00_find_fcport_by_wwpn(vha, e->u.new_sess.port_name, 1); + if (fcport) { + ql_log(ql_log_info, vha, 0x11020, + "Found fcport: %p for WWN: %8phC\n", fcport, + e->u.new_sess.port_name); + fcport->d_id = e->u.new_sess.id; + + /* Session existing with No loop_ID assigned */ + if (fcport->loop_id == FC_NO_LOOP_ID) { + fcport->loop_id = qla2x00_find_new_loop_id(vha, fcport); + ql_log(ql_log_info, vha, 0x11021, + "Allocated new loop_id: %#x for fcport: %p\n", + fcport->loop_id, fcport); + fcport->fw_login_state = DSC_LS_PLOGI_PEND; + } + } else { + fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (fcport) { + fcport->d_id = e->u.new_sess.id; + fcport->loop_id = qla2x00_find_new_loop_id(vha, fcport); + ql_log(ql_log_info, vha, 0x11022, + "Allocated new loop_id: %#x for fcport: %p\n", + fcport->loop_id, fcport); + + fcport->scan_state = QLA_FCPORT_FOUND; + fcport->flags |= FCF_FABRIC_DEVICE; + fcport->fw_login_state = DSC_LS_PLOGI_PEND; + + memcpy(fcport->port_name, e->u.new_sess.port_name, + WWN_SIZE); + + list_add_tail(&fcport->list, &vha->vp_fcports); + } + } + spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags); + + complete(&vha->purex_plogi_sess); +} void qla2x00_do_work(struct scsi_qla_host *vha) @@ -5129,6 +5185,10 @@ qla2x00_do_work(struct scsi_qla_host *vha) qla24xx_els_dcmd2_iocb(vha, ELS_DCMD_PLOGI, e->u.fcport.fcport, false); break; + /* FC-NVMe Target */ + case QLA_EVT_NEW_NVMET_SESS: + qla24xx_create_new_nvmet_sess(vha, e); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); @@ -6100,6 +6160,12 @@ qla2x00_do_dpc(void *data) set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); } + if (test_and_clear_bit(NVMET_PUREX, &base_vha->dpc_flags)) { + ql_log(ql_log_info, base_vha, 0x11022, + "qla2xxx-nvmet: Received a frame on the wire\n"); + qlt_dequeue_purex(base_vha); + } + if (test_and_clear_bit (ISP_ABORT_NEEDED, &base_vha->dpc_flags) && !test_bit(UNLOADING, &base_vha->dpc_flags)) { @@ -6273,6 +6339,13 @@ qla2x00_do_dpc(void *data) ha->nvme_last_rptd_aen); } } +#if (IS_ENABLED(CONFIG_NVME_TARGET_FC)) + if (test_and_clear_bit(NVMET_PUREX, &base_vha->dpc_flags)) { + ql_log(ql_log_info, base_vha, 0x11025, + "nvmet: Received a frame on the wire\n"); + qlt_dequeue_purex(base_vha); + } +#endif if (test_and_clear_bit(SET_ZIO_THRESHOLD_NEEDED, &base_vha->dpc_flags)) { diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index a69ec4519d81..6f61d4e04902 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -40,6 +40,7 @@ #include <target/target_core_fabric.h> #include "qla_def.h" +#include "qla_nvmet.h" #include "qla_target.h" static int ql2xtgt_tape_enable; @@ -78,6 +79,8 @@ int ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE; static int qla_sam_status = SAM_STAT_BUSY; static int tc_sam_status = SAM_STAT_TASK_SET_FULL; /* target core */ +int qlt_op_target_mode; + /* * From scsi/fc/fc_fcp.h */ @@ -149,11 +152,16 @@ static inline uint32_t qlt_make_handle(struct qla_qpair *); */ static struct kmem_cache *qla_tgt_mgmt_cmd_cachep; struct kmem_cache *qla_tgt_plogi_cachep; +static struct kmem_cache *qla_tgt_purex_plogi_cachep; static mempool_t *qla_tgt_mgmt_cmd_mempool; static struct workqueue_struct *qla_tgt_wq; +static struct workqueue_struct *qla_nvmet_wq; static DEFINE_MUTEX(qla_tgt_mutex); static LIST_HEAD(qla_tgt_glist); +/* WQ for nvmet completions */ +struct workqueue_struct *qla_nvmet_comp_wq; + static const char *prot_op_str(u32 prot_op) { switch (prot_op) { @@ -348,13 +356,653 @@ void qlt_unknown_atio_work_fn(struct work_struct *work) qlt_try_to_dequeue_unknown_atios(vha, 0); } +#define ELS_RJT 0x01 +#define ELS_ACC 0x02 + +struct fc_port *qla_nvmet_find_sess_by_s_id( + scsi_qla_host_t *vha, + const uint32_t s_id) +{ + struct fc_port *sess = NULL, *other_sess; + uint32_t other_sid; + + list_for_each_entry(other_sess, &vha->vp_fcports, list) { + other_sid = other_sess->d_id.b.domain << 16 | + other_sess->d_id.b.area << 8 | + other_sess->d_id.b.al_pa; + + if (other_sid == s_id) { + sess = other_sess; + break; + } + } + return sess; +} + +/* Send an ELS response */ +int qlt_send_els_resp(srb_t *sp, struct __els_pt *els_pkt) +{ + struct purex_entry_24xx *purex = (struct purex_entry_24xx *) + sp->u.snvme_els.ptr; + dma_addr_t udma = sp->u.snvme_els.dma_addr; + struct fc_port *fcport; + port_id_t port_id; + uint16_t loop_id; + + port_id.b.domain = purex->s_id[2]; + port_id.b.area = purex->s_id[1]; + port_id.b.al_pa = purex->s_id[0]; + port_id.b.rsvd_1 = 0; + + fcport = qla2x00_find_fcport_by_nportid(sp->vha, &port_id, 1); + if (fcport) + /* There is no session with the swt */ + loop_id = fcport->loop_id; + else + loop_id = 0xFFFF; + + ql_log(ql_log_info, sp->vha, 0xfff9, + "sp: %p, purex: %p, udma: %pad, loop_id: 0x%x\n", + sp, purex, &udma, loop_id); + + els_pkt->entry_type = ELS_IOCB_TYPE; + els_pkt->entry_count = 1; + + els_pkt->handle = sp->handle; + els_pkt->nphdl = cpu_to_le16(loop_id); + els_pkt->tx_dsd_cnt = cpu_to_le16(1); + els_pkt->vp_index = purex->vp_idx; + els_pkt->sof = EST_SOFI3; + els_pkt->rcv_exchg_id = cpu_to_le32(purex->rx_xchg_addr); + els_pkt->op_code = sp->cmd_type; + els_pkt->did_lo = cpu_to_le16(purex->s_id[0] | (purex->s_id[1] << 8)); + els_pkt->did_hi = purex->s_id[2]; + els_pkt->sid_hi = purex->d_id[2]; + els_pkt->sid_lo = cpu_to_le16(purex->d_id[0] | (purex->d_id[1] << 8)); + + if (sp->gen2 == ELS_ACC) + els_pkt->cntl_flags = cpu_to_le16(EPD_ELS_ACC); + else + els_pkt->cntl_flags = cpu_to_le16(EPD_ELS_RJT); + + els_pkt->tx_bc = cpu_to_le32(sp->gen1); + els_pkt->tx_dsd[0] = cpu_to_le32(LSD(udma)); + els_pkt->tx_dsd[1] = cpu_to_le32(MSD(udma)); + els_pkt->tx_dsd_len = cpu_to_le32(sp->gen1); + /* Memory Barrier */ + wmb(); + + ql_log(ql_log_info, sp->vha, 0x11030, "Dumping PLOGI ELS\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, sp->vha, 0xffff, + (uint8_t *)els_pkt, sizeof(*els_pkt)); + + return 0; +} + +static void qlt_nvme_els_done(void *s, int res) +{ + struct srb *sp = s; + + ql_log(ql_log_info, sp->vha, 0x11031, + "Done with NVME els command\n"); + + ql_log(ql_log_info, sp->vha, 0x11032, + "sp: %p vha: %p, dma_ptr: %p, dma_addr: %pad, len: %#x\n", + sp, sp->vha, sp->u.snvme_els.dma_ptr, &sp->u.snvme_els.dma_addr, + sp->gen1); + + qla2x00_rel_sp(sp); +} + +static int qlt_send_plogi_resp(struct scsi_qla_host *vha, uint8_t op_code, + struct purex_entry_24xx *purex, struct fc_port *fcport) +{ + int ret, rval, i; + dma_addr_t plogi_ack_udma = vha->vha_tgt.qla_tgt->nvme_els_rsp; + void *plogi_ack_buf = vha->vha_tgt.qla_tgt->nvme_els_ptr; + uint8_t *tmp; + uint32_t *opcode; + srb_t *sp; + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) { + ql_log(ql_log_info, vha, 0x11033, + "Failed to allocate SRB\n"); + return -ENOMEM; + } + + sp->type = SRB_NVME_ELS_RSP; + sp->done = qlt_nvme_els_done; + sp->vha = vha; + + ql_log(ql_log_info, vha, 0x11034, + "sp: %p, vha: %p, plogi_ack_buf: %p\n", + sp, vha, plogi_ack_buf); + + sp->u.snvme_els.dma_addr = plogi_ack_udma; + sp->u.snvme_els.dma_ptr = plogi_ack_buf; + sp->gen1 = 116; + sp->gen2 = ELS_ACC; + sp->u.snvme_els.ptr = (struct purex_entry_24xx *)purex; + sp->cmd_type = ELS_PLOGI; + + tmp = (uint8_t *)plogi_ack_udma; + + tmp += 4; /* fw doesn't return 1st 4 bytes where opcode goes */ + + ret = qla2x00_get_plogi_template(vha, (dma_addr_t)tmp, (116/4 - 1)); + if (ret) { + ql_log(ql_log_warn, vha, 0x11035, + "Failed to get plogi template\n"); + return -ENOMEM; + } + + opcode = (uint32_t *) plogi_ack_buf; + *opcode = cpu_to_be32(ELS_ACC << 24); + + for (i = 0; i < 0x1c; i++) { + ++opcode; + *opcode = cpu_to_be32(*opcode); + } + + ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0xfff3, + "Dumping the PLOGI from fw\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_verbose, vha, 0x70cf, + (uint8_t *)plogi_ack_buf, 116); + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + qla2x00_rel_sp(sp); + + return 0; +} + +static struct qlt_purex_plogi_ack_t * +qlt_plogi_find_add(struct scsi_qla_host *vha, port_id_t *id, + struct __fc_plogi *rcvd_plogi) +{ + struct qlt_purex_plogi_ack_t *pla; + + list_for_each_entry(pla, &vha->plogi_ack_list, list) { + if (pla->id.b24 == id->b24) + return pla; + } + + pla = kmem_cache_zalloc(qla_tgt_purex_plogi_cachep, GFP_ATOMIC); + if (!pla) { + ql_dbg(ql_dbg_async, vha, 0x5088, + "qla_target(%d): Allocation of plogi_ack failed\n", + vha->vp_idx); + return NULL; + } + + pla->id = *id; + memcpy(&pla->rcvd_plogi, rcvd_plogi, sizeof(struct __fc_plogi)); + ql_log(ql_log_info, vha, 0xf101, + "New session(%p) created for port: %#x\n", + pla, pla->id.b24); + + list_add_tail(&pla->list, &vha->plogi_ack_list); + + return pla; +} + +static void __swap_wwn(uint8_t *ptr, uint32_t size) +{ + uint32_t *iptr = (uint32_t *)ptr; + uint32_t *optr = (uint32_t *)ptr; + uint32_t i = size >> 2; + + for (; i ; i--) + *optr++ = be32_to_cpu(*iptr++); +} + +static int abort_cmds_for_s_id(struct scsi_qla_host *vha, port_id_t *s_id); +/* + * Parse the PLOGI from the peer port + * Retrieve WWPN, WWNN from the payload + * Create and fc port if it is a new WWN + * else clean up the prev exchange + * Return a response + */ +static void qlt_process_plogi(struct scsi_qla_host *vha, + struct purex_entry_24xx *purex, void *buf) +{ + uint64_t pname, nname; + struct __fc_plogi *rcvd_plogi = (struct __fc_plogi *)buf; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + uint16_t loop_id; + unsigned long flags; + struct fc_port *sess = NULL, *conflict_sess = NULL; + struct qlt_purex_plogi_ack_t *pla; + port_id_t port_id; + int sess_handling = 0; + + port_id.b.domain = purex->s_id[2]; + port_id.b.area = purex->s_id[1]; + port_id.b.al_pa = purex->s_id[0]; + port_id.b.rsvd_1 = 0; + + if (IS_SW_RESV_ADDR(port_id)) { + ql_log(ql_log_info, vha, 0x11036, + "Received plogi from switch, just send an ACC\n"); + goto send_plogi_resp; + } + + loop_id = le16_to_cpu(purex->nport_handle); + + /* Clean up prev commands if any */ + if (sess_handling) { + ql_log(ql_log_info, vha, 0x11037, + "%s %d Cleaning up prev commands\n", + __func__, __LINE__); + abort_cmds_for_s_id(vha, &port_id); + } + + __swap_wwn(rcvd_plogi->pname, 4); + __swap_wwn(&rcvd_plogi->pname[4], 4); + pname = wwn_to_u64(rcvd_plogi->pname); + + __swap_wwn(rcvd_plogi->nname, 4); + __swap_wwn(&rcvd_plogi->nname[4], 4); + nname = wwn_to_u64(rcvd_plogi->nname); + + ql_log(ql_log_info, vha, 0x11038, + "%s %d, pname:%llx, nname:%llx port_id: %#x\n", + __func__, __LINE__, pname, nname, loop_id); + + /* Invalidate other sessions if any */ + spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags); + sess = qlt_find_sess_invalidate_other(vha, pname, + port_id, loop_id, &conflict_sess); + spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags); + + /* Add the inbound plogi(if from a new device) to the list */ + pla = qlt_plogi_find_add(vha, &port_id, rcvd_plogi); + + /* If there is no existing session, create one */ + if (unlikely(!sess)) { + ql_log(ql_log_info, vha, 0xf102, + "Creating a new session\n"); + init_completion(&vha->purex_plogi_sess); + qla24xx_post_nvmet_newsess_work(vha, &port_id, + rcvd_plogi->pname, pla); + wait_for_completion_timeout(&vha->purex_plogi_sess, 500); + /* Send a PLOGI response */ + goto send_plogi_resp; + } else { + /* Session existing with No loop_ID assigned */ + if (sess->loop_id == FC_NO_LOOP_ID) { + sess->loop_id = qla2x00_find_new_loop_id(vha, sess); + ql_log(ql_log_info, vha, 0x11039, + "Allocated new loop_id: %#x for fcport: %p\n", + sess->loop_id, sess); + } + sess->d_id = port_id; + + sess->fw_login_state = DSC_LS_PLOGI_PEND; + } +send_plogi_resp: + /* Send a PLOGI response */ + qlt_send_plogi_resp(vha, ELS_PLOGI, purex, sess); +} + +static int qlt_process_logo(struct scsi_qla_host *vha, + struct purex_entry_24xx *purex, void *buf) +{ + struct __fc_logo_acc *logo_acc; + dma_addr_t logo_ack_udma = vha->vha_tgt.qla_tgt->nvme_els_rsp; + void *logo_ack_buf = vha->vha_tgt.qla_tgt->nvme_els_ptr; + srb_t *sp; + int rval; + uint32_t look_up_sid; + fc_port_t *sess = NULL; + port_id_t port_id; + + port_id.b.domain = purex->s_id[2]; + port_id.b.area = purex->s_id[1]; + port_id.b.al_pa = purex->s_id[0]; + port_id.b.rsvd_1 = 0; + + if (!IS_SW_RESV_ADDR(port_id)) { + look_up_sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | + purex->s_id[0]; + ql_log(ql_log_info, vha, 0x11040, + "%s - Look UP sid: %#x\n", __func__, look_up_sid); + + sess = qla_nvmet_find_sess_by_s_id(vha, look_up_sid); + if (unlikely(!sess)) + WARN_ON(1); + } + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL); + if (!sp) { + ql_log(ql_log_info, vha, 0x11041, + "Failed to allocate SRB\n"); + return -ENOMEM; + } + + sp->type = SRB_NVME_ELS_RSP; + sp->done = qlt_nvme_els_done; + sp->vha = vha; + sp->fcport = sess; + + ql_log(ql_log_info, vha, 0x11042, + "sp: %p, vha: %p, logo_ack_buf: %p\n", + sp, vha, logo_ack_buf); + + logo_acc = (struct __fc_logo_acc *)logo_ack_buf; + memset(logo_acc, 0, sizeof(*logo_acc)); + logo_acc->op_code = ELS_ACC; + + /* Send response */ + sp->u.snvme_els.dma_addr = logo_ack_udma; + sp->u.snvme_els.dma_ptr = logo_ack_buf; + sp->gen1 = sizeof(struct __fc_logo_acc); + sp->gen2 = ELS_ACC; + sp->u.snvme_els.ptr = (struct purex_entry_24xx *)purex; + sp->cmd_type = ELS_LOGO; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + qla2x00_rel_sp(sp); + + return 0; +} + +static int qlt_process_prli(struct scsi_qla_host *vha, + struct purex_entry_24xx *purex, void *buf) +{ + struct __fc_prli *prli = (struct __fc_prli *)buf; + struct __fc_prli_acc *prli_acc; + struct __fc_prli_rjt *prli_rej; + dma_addr_t prli_ack_udma = vha->vha_tgt.qla_tgt->nvme_els_rsp; + void *prli_ack_buf = vha->vha_tgt.qla_tgt->nvme_els_ptr; + srb_t *sp; + struct fc_port *sess = NULL; + int rval; + uint32_t look_up_sid; + port_id_t port_id; + + port_id.b.domain = purex->s_id[2]; + port_id.b.area = purex->s_id[1]; + port_id.b.al_pa = purex->s_id[0]; + port_id.b.rsvd_1 = 0; + + if (!IS_SW_RESV_ADDR(port_id)) { + look_up_sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | + purex->s_id[0]; + ql_log(ql_log_info, vha, 0x11043, + "%s - Look UP sid: %#x\n", __func__, look_up_sid); + + sess = qla_nvmet_find_sess_by_s_id(vha, look_up_sid); + if (unlikely(!sess)) + WARN_ON(1); + } + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL); + if (!sp) { + ql_log(ql_log_info, vha, 0x11044, + "Failed to allocate SRB\n"); + return -ENOMEM; + } + + sp->type = SRB_NVME_ELS_RSP; + sp->done = qlt_nvme_els_done; + sp->vha = vha; + sp->fcport = sess; + + ql_log(ql_log_info, vha, 0x11045, + "sp: %p, vha: %p, prli_ack_buf: %p, prli_ack_udma: %pad\n", + sp, vha, prli_ack_buf, &prli_ack_udma); + + memset(prli_ack_buf, 0, sizeof(struct __fc_prli_acc)); + + /* Parse PRLI */ + if (prli->prli_type == PRLI_TYPE_FCP) { + /* Send a RJT for FCP */ + prli_rej = (struct __fc_prli_rjt *)prli_ack_buf; + prli_rej->op_code = ELS_RJT; + prli_rej->reason = PRLI_RJT_REASON; + } else if (prli->prli_type == PRLI_TYPE_NVME) { + uint32_t spinfo; + + prli_acc = (struct __fc_prli_acc *)prli_ack_buf; + prli_acc->op_code = ELS_ACC; + prli_acc->type = PRLI_TYPE_NVME; + prli_acc->page_length = PRLI_NVME_PAGE_LENGTH; + prli_acc->common = cpu_to_be16(PRLI_REQ_EXEC); + prli_acc->pyld_length = cpu_to_be16(PRLI_ACC_NVME_RESP_LEN); + spinfo = NVME_PRLI_DISC | NVME_PRLI_TRGT; + prli_acc->nvme.sp_info = cpu_to_be32(spinfo); + } + + /* Send response */ + sp->u.snvme_els.dma_addr = prli_ack_udma; + sp->u.snvme_els.dma_ptr = prli_ack_buf; + + if (prli->prli_type == PRLI_TYPE_FCP) { + sp->gen1 = sizeof(struct __fc_prli_rjt); + sp->gen2 = ELS_RJT; + } else if (prli->prli_type == PRLI_TYPE_NVME) { + sp->gen1 = sizeof(struct __fc_prli_acc); + sp->gen2 = ELS_ACC; + } + + sp->u.snvme_els.ptr = (struct purex_entry_24xx *)purex; + sp->cmd_type = ELS_PRLI; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + qla2x00_rel_sp(sp); + + return 0; +} + +static void *qlt_get_next_atio_pkt(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + void *pkt; + + ha->tgt.atio_ring_index++; + if (ha->tgt.atio_ring_index == ha->tgt.atio_q_length) { + ha->tgt.atio_ring_index = 0; + ha->tgt.atio_ring_ptr = ha->tgt.atio_ring; + } else { + ha->tgt.atio_ring_ptr++; + } + pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; + + return pkt; +} + +static void qlt_process_purex(struct scsi_qla_host *vha, + struct qla_tgt_purex_op *p) +{ + struct atio_from_isp *atio = &p->atio; + struct purex_entry_24xx *purex = + (struct purex_entry_24xx *)&atio->u.raw; + uint16_t len = purex->frame_size; + + ql_log(ql_log_info, vha, 0xf100, + "Purex IOCB: EC:%#x, Len:%#x ELS_OP:%#x oxid:%#x rxid:%#x\n", + purex->entry_count, len, purex->pyld[3], + purex->ox_id, purex->rx_id); + + switch (purex->pyld[3]) { + case ELS_PLOGI: + qlt_process_plogi(vha, purex, p->purex_pyld); + break; + case ELS_PRLI: + qlt_process_prli(vha, purex, p->purex_pyld); + break; + case ELS_LOGO: + qlt_process_logo(vha, purex, p->purex_pyld); + break; + default: + ql_log(ql_log_warn, vha, 0x11046, + "Unexpected ELS 0x%x\n", purex->pyld[3]); + break; + } +} + +void qlt_dequeue_purex(struct scsi_qla_host *vha) +{ + struct qla_tgt_purex_op *p, *t; + unsigned long flags; + + list_for_each_entry_safe(p, t, &vha->purex_atio_list, cmd_list) { + ql_log(ql_log_info, vha, 0xff1e, + "Processing ATIO %p\n", &p->atio); + + qlt_process_purex(vha, p); + spin_lock_irqsave(&vha->cmd_list_lock, flags); + list_del(&p->cmd_list); + spin_unlock_irqrestore(&vha->cmd_list_lock, flags); + kfree(p->purex_pyld); + kfree(p); + } +} + +static void qlt_queue_purex(scsi_qla_host_t *vha, + struct atio_from_isp *atio) +{ + struct qla_tgt_purex_op *p; + unsigned long flags; + struct purex_entry_24xx *purex = + (struct purex_entry_24xx *)&atio->u.raw; + uint16_t len = purex->frame_size; + uint8_t *purex_pyld_tmp; + + p = kzalloc(sizeof(*p), GFP_ATOMIC); + if (p == NULL) + goto out; + + p->vha = vha; + memcpy(&p->atio, atio, sizeof(*atio)); + + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0xff11, + "Dumping the Purex IOCB received\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0xe012, + (uint8_t *)purex, 64); + + p->purex_pyld = kzalloc(sizeof(purex->entry_count) * 64, GFP_ATOMIC); + if (p->purex_pyld == NULL) { + kfree(p); + goto out; + } + purex_pyld_tmp = (uint8_t *)p->purex_pyld; + p->purex_pyld_len = len; + + if (len < PUREX_PYLD_SIZE) + len = PUREX_PYLD_SIZE; + + memcpy(p->purex_pyld, &purex->d_id, PUREX_PYLD_SIZE); + purex_pyld_tmp += PUREX_PYLD_SIZE; + len -= PUREX_PYLD_SIZE; + + while (len > 0) { + int cpylen; + struct __status_cont *cont_atio; + + cont_atio = (struct __status_cont *)qlt_get_next_atio_pkt(vha); + cpylen = len > CONT_SENSE_DATA ? CONT_SENSE_DATA : len; + ql_log(ql_log_info, vha, 0xff12, + "cont_atio: %p, cpylen: %#x\n", cont_atio, cpylen); + + memcpy(purex_pyld_tmp, &cont_atio->data[0], cpylen); + + purex_pyld_tmp += cpylen; + len -= cpylen; + } + + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0xff11, + "Dumping the Purex IOCB(%p) received\n", p->purex_pyld); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0xe011, + (uint8_t *)p->purex_pyld, p->purex_pyld_len); + + INIT_LIST_HEAD(&p->cmd_list); + + spin_lock_irqsave(&vha->cmd_list_lock, flags); + list_add_tail(&p->cmd_list, &vha->purex_atio_list); + spin_unlock_irqrestore(&vha->cmd_list_lock, flags); + +out: + return; +} + +static void sys_to_be32_cpy(uint8_t *dest, uint8_t *src, uint16_t len) +{ + uint32_t *d, *s, i; + + d = (uint32_t *) dest; + s = (uint32_t *) src; + for (i = 0; i < len; i++) + d[i] = cpu_to_be32(s[i]); +} + +/* Prepare an LS req received from the wire to be sent to the nvmet */ +static void *qlt_nvmet_prepare_ls(struct scsi_qla_host *vha, + struct pt_ls4_rx_unsol *ls4) +{ + int desc_len = cpu_to_le16(ls4->desc_len) + 8; + int copy_len, bc; + void *buf; + uint8_t *cpy_buf; + int i; + struct __status_cont *cont_atio; + + ql_dbg(ql_dbg_tgt, vha, 0xe072, + "%s: desc_len:%d\n", __func__, desc_len); + + buf = kzalloc(desc_len, GFP_ATOMIC); + if (!buf) + return NULL; + + cpy_buf = buf; + bc = desc_len; + + if (bc < PT_LS4_FIRST_PACKET_LEN) + copy_len = bc; + else + copy_len = PT_LS4_FIRST_PACKET_LEN; + + sys_to_be32_cpy(cpy_buf, &((uint8_t *)ls4)[PT_LS4_PAYLOAD_OFFSET], + copy_len/4); + + bc -= copy_len; + cpy_buf += copy_len; + + cont_atio = (struct __status_cont *)ls4; + + for (i = 1; i < ls4->entry_count && bc > 0; i++) { + if (bc < CONT_SENSE_DATA) + copy_len = bc; + else + copy_len = CONT_SENSE_DATA; + + cont_atio = (struct __status_cont *)qlt_get_next_atio_pkt(vha); + + sys_to_be32_cpy(cpy_buf, (uint8_t *)&cont_atio->data, + copy_len/4); + cpy_buf += copy_len; + bc -= copy_len; + } + + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0xc0f1, + "Dump the first 128 bytes of LS request\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075, + (uint8_t *)buf, 128); + + return buf; +} + static bool qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, struct atio_from_isp *atio, uint8_t ha_locked) { - ql_dbg(ql_dbg_tgt, vha, 0xe072, - "%s: qla_target(%d): type %x ox_id %04x\n", - __func__, vha->vp_idx, atio->u.raw.entry_type, - be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id)); + void *buf; switch (atio->u.raw.entry_type) { case ATIO_TYPE7: @@ -414,31 +1062,74 @@ static bool qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, { struct abts_recv_from_24xx *entry = (struct abts_recv_from_24xx *)atio; - struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha, - entry->vp_index); - unsigned long flags; - if (unlikely(!host)) { - ql_dbg(ql_dbg_tgt, vha, 0xe00a, - "qla_target(%d): Response pkt (ABTS_RECV_24XX) " - "received, with unknown vp_index %d\n", - vha->vp_idx, entry->vp_index); + if (unlikely(atio->u.nvme_isp27.fcnvme_hdr.scsi_fc_id == + NVMEFC_CMD_IU_SCSI_FC_ID)) { + qla_nvmet_handle_abts(vha, entry); + break; + } + + { + struct abts_recv_from_24xx *entry = + (struct abts_recv_from_24xx *)atio; + struct scsi_qla_host *host = qlt_find_host_by_vp_idx + (vha, entry->vp_index); + unsigned long flags; + + if (unlikely(!host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe00a, + "qla_target(%d): Response pkt (ABTS_RECV_24XX) received, with unknown vp_index %d\n", + vha->vp_idx, entry->vp_index); + break; + } + if (!ha_locked) + spin_lock_irqsave(&host->hw->hardware_lock, + flags); + qlt_24xx_handle_abts(host, + (struct abts_recv_from_24xx *)atio); + if (!ha_locked) + spin_unlock_irqrestore( + &host->hw->hardware_lock, flags); break; } - if (!ha_locked) - spin_lock_irqsave(&host->hw->hardware_lock, flags); - qlt_24xx_handle_abts(host, (struct abts_recv_from_24xx *)atio); - if (!ha_locked) - spin_unlock_irqrestore(&host->hw->hardware_lock, flags); - break; } - /* case PUREX_IOCB_TYPE: ql2xmvasynctoatio */ + /* NVME */ + case ATIO_PURLS: + { + struct scsi_qla_host *host = vha; + unsigned long flags; + + /* Received an LS4 from the init, pass it to the NVMEt */ + ql_log(ql_log_info, vha, 0x11047, + "%s %d Received an LS4 from the initiator on ATIO\n", + __func__, __LINE__); + spin_lock_irqsave(&host->hw->hardware_lock, flags); + buf = qlt_nvmet_prepare_ls(host, + (struct pt_ls4_rx_unsol *)atio); + if (buf) + qla_nvmet_handle_ls(host, + (struct pt_ls4_rx_unsol *)atio, buf); + spin_unlock_irqrestore(&host->hw->hardware_lock, flags); + } + break; + + case PUREX_IOCB_TYPE: /* NVMET */ + { + /* Received a PUREX IOCB */ + /* Queue the iocb and wake up dpc */ + qlt_queue_purex(vha, atio); + set_bit(NVMET_PUREX, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + break; + } default: ql_dbg(ql_dbg_tgt, vha, 0xe040, "qla_target(%d): Received unknown ATIO atio " "type %x\n", vha->vp_idx, atio->u.raw.entry_type); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0xe011, + (uint8_t *)atio, sizeof(*atio)); break; } @@ -541,6 +1232,10 @@ void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, break; } qlt_response_pkt(host, rsp, pkt); + if (unlikely(qlt_op_target_mode)) + qla24xx_nvmet_abts_resp_iocb(vha, + (struct abts_resp_to_24xx *)pkt, + rsp->req); break; } default: @@ -1623,6 +2318,11 @@ static void qlt_release(struct qla_tgt *tgt) vha->vha_tgt.target_lport_ptr) ha->tgt.tgt_ops->remove_target(vha); + if (tgt->nvme_els_ptr) { + dma_free_coherent(&vha->hw->pdev->dev, 256, + tgt->nvme_els_ptr, tgt->nvme_els_rsp); + } + vha->vha_tgt.qla_tgt = NULL; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00d, @@ -5648,6 +6348,101 @@ qlt_chk_qfull_thresh_hold(struct scsi_qla_host *vha, struct qla_qpair *qpair, return 1; } +/* + * Worker thread that dequeues the nvme cmd off the list and + * called nvme-t to process the cmd + */ +static void qla_nvmet_work(struct work_struct *work) +{ + struct qla_nvmet_cmd *cmd = + container_of(work, struct qla_nvmet_cmd, work); + scsi_qla_host_t *vha = cmd->vha; + + qla_nvmet_process_cmd(vha, cmd); +} +/* + * Handle the NVME cmd IU + */ +static void qla_nvmet_handle_cmd(struct scsi_qla_host *vha, + struct atio_from_isp *atio) +{ + struct qla_nvmet_cmd *tgt_cmd; + unsigned long flags; + struct qla_hw_data *ha = vha->hw; + struct fc_port *fcport; + struct fcp_hdr *fcp_hdr; + uint32_t s_id = 0; + void *next_pkt; + uint8_t *nvmet_cmd_ptr; + uint32_t nvmet_cmd_iulen = 0; + uint32_t nvmet_cmd_iulen_min = 64; + + /* Create an NVME cmd and queue it up to the work queue */ + tgt_cmd = kzalloc(sizeof(struct qla_nvmet_cmd), GFP_ATOMIC); + if (tgt_cmd == NULL) + return; + + tgt_cmd->vha = vha; + + fcp_hdr = &atio->u.nvme_isp27.fcp_hdr; + + /* Get the session for this command */ + s_id = fcp_hdr->s_id[0] << 16 | fcp_hdr->s_id[1] << 8 + | fcp_hdr->s_id[2]; + tgt_cmd->ox_id = fcp_hdr->ox_id; + + fcport = qla_nvmet_find_sess_by_s_id(vha, s_id); + if (unlikely(!fcport)) { + ql_log(ql_log_warn, vha, 0x11049, + "Cant' find the session for port_id: %#x\n", s_id); + kfree(tgt_cmd); + return; + } + + tgt_cmd->fcport = fcport; + + memcpy(&tgt_cmd->atio, atio, sizeof(*atio)); + + /* The FC-NMVE cmd covers 2 ATIO IOCBs */ + + nvmet_cmd_ptr = (uint8_t *)&tgt_cmd->nvme_cmd_iu; + nvmet_cmd_iulen = be16_to_cpu(atio->u.nvme_isp27.fcnvme_hdr.iu_len) * 4; + tgt_cmd->cmd_len = nvmet_cmd_iulen; + + if (unlikely(ha->tgt.atio_ring_index + atio->u.raw.entry_count > + ha->tgt.atio_q_length)) { + uint8_t i; + + memcpy(nvmet_cmd_ptr, &((uint8_t *)atio)[NVME_ATIO_CMD_OFF], + ATIO_NVME_FIRST_PACKET_CMDLEN); + nvmet_cmd_ptr += ATIO_NVME_FIRST_PACKET_CMDLEN; + nvmet_cmd_iulen -= ATIO_NVME_FIRST_PACKET_CMDLEN; + + for (i = 1; i < atio->u.raw.entry_count; i++) { + uint8_t cplen = min(nvmet_cmd_iulen_min, + nvmet_cmd_iulen); + + next_pkt = qlt_get_next_atio_pkt(vha); + memcpy(nvmet_cmd_ptr, (uint8_t *)next_pkt, cplen); + nvmet_cmd_ptr += cplen; + nvmet_cmd_iulen -= cplen; + } + } else { + memcpy(nvmet_cmd_ptr, &((uint8_t *)atio)[NVME_ATIO_CMD_OFF], + nvmet_cmd_iulen); + next_pkt = qlt_get_next_atio_pkt(vha); + } + + /* Add cmd to the list */ + spin_lock_irqsave(&vha->cmd_list_lock, flags); + list_add_tail(&tgt_cmd->cmd_list, &vha->qla_cmd_list); + spin_unlock_irqrestore(&vha->cmd_list_lock, flags); + + /* Queue the work item */ + INIT_WORK(&tgt_cmd->work, qla_nvmet_work); + queue_work(qla_nvmet_wq, &tgt_cmd->work); +} + /* ha->hardware_lock supposed to be held on entry */ /* called via callback from qla2xxx */ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, @@ -5687,6 +6482,13 @@ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, break; } + /* NVME Target*/ + if (unlikely(atio->u.nvme_isp27.fcnvme_hdr.scsi_fc_id + == NVMEFC_CMD_IU_SCSI_FC_ID)) { + qla_nvmet_handle_cmd(vha, atio); + break; + } + if (likely(atio->u.isp24.fcp_cmnd.task_mgmt_flags == 0)) { rc = qlt_chk_qfull_thresh_hold(vha, ha->base_qpair, atio, ha_locked); @@ -6537,6 +7339,14 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) if (ha->tgt.tgt_ops && ha->tgt.tgt_ops->add_target) ha->tgt.tgt_ops->add_target(base_vha); + tgt->nvme_els_ptr = dma_alloc_coherent(&base_vha->hw->pdev->dev, 256, + &tgt->nvme_els_rsp, GFP_KERNEL); + if (!tgt->nvme_els_ptr) { + ql_dbg(ql_dbg_tgt, base_vha, 0xe066, + "Unable to allocate DMA buffer for NVME ELS request\n"); + return -ENOMEM; + } + return 0; } @@ -6831,6 +7641,7 @@ qlt_rff_id(struct scsi_qla_host *vha) u8 fc4_feature = 0; /* * FC-4 Feature bit 0 indicates target functionality to the name server. + * NVME FC-4 Feature bit 2 indicates discovery controller */ if (qla_tgt_mode_enabled(vha)) { fc4_feature = BIT_0; @@ -6868,6 +7679,76 @@ qlt_init_atio_q_entries(struct scsi_qla_host *vha) } +static void +qlt_27xx_process_nvme_atio_queue(struct scsi_qla_host *vha, uint8_t ha_locked) +{ + struct qla_hw_data *ha = vha->hw; + struct atio_from_isp *pkt; + int cnt; + uint32_t atio_q_in; + uint16_t num_atios = 0; + uint8_t nvme_pkts = 0; + + if (!ha->flags.fw_started) + return; + + pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; + while (num_atios < pkt->u.raw.entry_count) { + atio_q_in = RD_REG_DWORD(ISP_ATIO_Q_IN(vha)); + if (atio_q_in < ha->tgt.atio_ring_index) + num_atios = ha->tgt.atio_q_length - + (ha->tgt.atio_ring_index - atio_q_in); + else + num_atios = atio_q_in - ha->tgt.atio_ring_index; + if (num_atios == 0) + return; + } + + while ((num_atios) || fcpcmd_is_corrupted(ha->tgt.atio_ring_ptr)) { + pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; + cnt = pkt->u.raw.entry_count; + + if (unlikely(fcpcmd_is_corrupted(ha->tgt.atio_ring_ptr))) { + /* + * This packet is corrupted. The header + payload + * can not be trusted. There is no point in passing + * it further up. + */ + ql_log(ql_log_warn, vha, 0xd03c, + "corrupted fcp frame SID[%3phN] OXID[%04x] EXCG[%x] %64phN\n", + pkt->u.isp24.fcp_hdr.s_id, + be16_to_cpu(pkt->u.isp24.fcp_hdr.ox_id), + le32_to_cpu(pkt->u.isp24.exchange_addr), pkt); + + adjust_corrupted_atio(pkt); + qlt_send_term_exchange(ha->base_qpair, NULL, pkt, + ha_locked, 0); + } else { + qlt_24xx_atio_pkt_all_vps(vha, + (struct atio_from_isp *)pkt, ha_locked); + nvme_pkts++; + } + + /* Just move by one index since we have already accounted the + * additional ones while processing individual ATIOs + */ + ha->tgt.atio_ring_index++; + if (ha->tgt.atio_ring_index == ha->tgt.atio_q_length) { + ha->tgt.atio_ring_index = 0; + ha->tgt.atio_ring_ptr = ha->tgt.atio_ring; + } else + ha->tgt.atio_ring_ptr++; + + pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; + num_atios -= cnt; + /* memory barrier */ + wmb(); + } + + /* Adjust ring index */ + WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), ha->tgt.atio_ring_index); +} + /* * qlt_24xx_process_atio_queue() - Process ATIO queue entries. * @ha: SCSI driver HA context @@ -6879,9 +7760,15 @@ qlt_24xx_process_atio_queue(struct scsi_qla_host *vha, uint8_t ha_locked) struct atio_from_isp *pkt; int cnt, i; + if (unlikely(qlt_op_target_mode)) { + qlt_27xx_process_nvme_atio_queue(vha, ha_locked); + return; + } + if (!ha->flags.fw_started) return; + pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; while ((ha->tgt.atio_ring_ptr->signature != ATIO_PROCESSED) || fcpcmd_is_corrupted(ha->tgt.atio_ring_ptr)) { pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; @@ -6907,6 +7794,7 @@ qlt_24xx_process_atio_queue(struct scsi_qla_host *vha, uint8_t ha_locked) (struct atio_from_isp *)pkt, ha_locked); } + cnt = 1; for (i = 0; i < cnt; i++) { ha->tgt.atio_ring_index++; if (ha->tgt.atio_ring_index == ha->tgt.atio_q_length) { @@ -6918,11 +7806,13 @@ qlt_24xx_process_atio_queue(struct scsi_qla_host *vha, uint8_t ha_locked) pkt->u.raw.signature = ATIO_PROCESSED; pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; } + /* memory barrier */ wmb(); } /* Adjust ring index */ WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), ha->tgt.atio_ring_index); + RD_REG_DWORD_RELAXED(ISP_ATIO_Q_OUT(vha)); } void @@ -7219,6 +8109,9 @@ qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha) INIT_DELAYED_WORK(&base_vha->unknown_atio_work, qlt_unknown_atio_work_fn); + /* NVMET */ + INIT_LIST_HEAD(&base_vha->purex_atio_list); + qlt_clear_mode(base_vha); rc = btree_init32(&ha->tgt.host_map); @@ -7445,13 +8338,25 @@ int __init qlt_init(void) goto out_mgmt_cmd_cachep; } + qla_tgt_purex_plogi_cachep = + kmem_cache_create("qla_tgt_purex_plogi_cachep", + sizeof(struct qlt_purex_plogi_ack_t), + __alignof__(struct qlt_purex_plogi_ack_t), 0, NULL); + + if (!qla_tgt_purex_plogi_cachep) { + ql_log(ql_log_fatal, NULL, 0xe06d, + "kmem_cache_create for qla_tgt_purex_plogi_cachep failed\n"); + ret = -ENOMEM; + goto out_plogi_cachep; + } + qla_tgt_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab, mempool_free_slab, qla_tgt_mgmt_cmd_cachep); if (!qla_tgt_mgmt_cmd_mempool) { ql_log(ql_log_fatal, NULL, 0xe06e, "mempool_create for qla_tgt_mgmt_cmd_mempool failed\n"); ret = -ENOMEM; - goto out_plogi_cachep; + goto out_purex_plogi_cachep; } qla_tgt_wq = alloc_workqueue("qla_tgt_wq", 0, 0); @@ -7461,6 +8366,25 @@ int __init qlt_init(void) ret = -ENOMEM; goto out_cmd_mempool; } + + qla_nvmet_wq = alloc_workqueue("qla_nvmet_wq", 0, 0); + if (!qla_nvmet_wq) { + ql_log(ql_log_fatal, NULL, 0xe070, + "alloc_workqueue for qla_nvmet_wq failed\n"); + ret = -ENOMEM; + destroy_workqueue(qla_tgt_wq); + goto out_cmd_mempool; + } + + qla_nvmet_comp_wq = alloc_workqueue("qla_nvmet_comp_wq", 0, 0); + if (!qla_nvmet_comp_wq) { + ql_log(ql_log_fatal, NULL, 0xe071, + "alloc_workqueue for qla_nvmet_wq failed\n"); + ret = -ENOMEM; + destroy_workqueue(qla_nvmet_wq); + destroy_workqueue(qla_tgt_wq); + goto out_cmd_mempool; + } /* * Return 1 to signal that initiator-mode is being disabled */ @@ -7468,6 +8392,8 @@ int __init qlt_init(void) out_cmd_mempool: mempool_destroy(qla_tgt_mgmt_cmd_mempool); +out_purex_plogi_cachep: + kmem_cache_destroy(qla_tgt_purex_plogi_cachep); out_plogi_cachep: kmem_cache_destroy(qla_tgt_plogi_cachep); out_mgmt_cmd_cachep: @@ -7480,8 +8406,19 @@ void qlt_exit(void) if (!QLA_TGT_MODE_ENABLED()) return; + destroy_workqueue(qla_nvmet_comp_wq); + destroy_workqueue(qla_nvmet_wq); destroy_workqueue(qla_tgt_wq); mempool_destroy(qla_tgt_mgmt_cmd_mempool); kmem_cache_destroy(qla_tgt_plogi_cachep); + kmem_cache_destroy(qla_tgt_purex_plogi_cachep); kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep); } + +void nvmet_release_sessions(struct scsi_qla_host *vha) +{ + struct qlt_purex_plogi_ack_t *pla, *tpla; + + list_for_each_entry_safe(pla, tpla, &vha->plogi_ack_list, list) + list_del(&pla->list); +} diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index 721da593b1bc..fcb4c9bb4fc1 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -322,6 +322,67 @@ struct atio7_fcp_cmnd { /* uint32_t data_length; */ } __packed; +struct fc_nvme_hdr { + union { + struct { + uint8_t scsi_id; +#define NVMEFC_CMD_IU_SCSI_ID 0xfd + uint8_t fc_id; +#define NVMEFC_CMD_IU_FC_ID 0x28 + }; + struct { + uint16_t scsi_fc_id; +#define NVMEFC_CMD_IU_SCSI_FC_ID 0x28fd + }; + }; + uint16_t iu_len; + uint8_t rsv1[3]; + uint8_t flags; +#define NVMEFC_CMD_WRITE 0x1 +#define NVMEFC_CMD_READ 0x2 + uint64_t conn_id; + uint32_t csn; + uint32_t dl; +} __packed; + +struct atio7_nvme_cmnd { + struct fc_nvme_hdr fcnvme_hdr; + + struct nvme_command nvme_cmd; + uint32_t rsv2[2]; +} __packed; + +#define ATIO_PURLS 0x56 +struct pt_ls4_rx_unsol { + uint8_t entry_type; /* 0x56 */ + uint8_t entry_count; + uint16_t rsvd0; + uint16_t rsvd1; + uint8_t vp_index; + uint8_t rsvd2; + uint16_t rsvd3; + uint16_t nport_handle; + uint16_t frame_size; + uint16_t rsvd4; + uint32_t exchange_address; + uint8_t d_id[3]; + uint8_t r_ctl; + uint8_t s_id[3]; + uint8_t cs_ctl; + uint8_t f_ctl[3]; + uint8_t type; + uint16_t seq_cnt; + uint8_t df_ctl; + uint8_t seq_id; + uint16_t rx_id; + uint16_t ox_id; + uint32_t param; + uint32_t desc0; +#define PT_LS4_PAYLOAD_OFFSET 0x2c +#define PT_LS4_FIRST_PACKET_LEN 20 + uint32_t desc_len; + uint32_t payload[3]; +}; /* * ISP queue - Accept Target I/O (ATIO) type entry IOCB structure. * This is sent from the ISP to the target driver. @@ -368,6 +429,21 @@ struct atio_from_isp { uint32_t signature; #define ATIO_PROCESSED 0xDEADDEAD /* Signature */ } raw; + /* FC-NVME */ + struct { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t fcp_cmnd_len_low; + uint8_t fcp_cmnd_len_high:4; + uint8_t attr:4; + uint32_t exchange_addr; +#define ATIO_NVME_ATIO_CMD_OFF 32 +#define ATIO_NVME_FIRST_PACKET_CMDLEN (64 - ATIO_NVME_ATIO_CMD_OFF) + struct fcp_hdr fcp_hdr; + struct fc_nvme_hdr fcnvme_hdr; + uint8_t nvmd_cmd[8]; + } nvme_isp27; + struct pt_ls4_rx_unsol pt_ls4; } u; } __packed; @@ -836,6 +912,8 @@ struct qla_tgt { int modify_lun_expected; atomic_t tgt_global_resets_count; struct list_head tgt_list_entry; + dma_addr_t nvme_els_rsp; + void *nvme_els_ptr; }; struct qla_tgt_sess_op { @@ -848,6 +926,16 @@ struct qla_tgt_sess_op { struct rsp_que *rsp; }; +/* NVMET */ +struct qla_tgt_purex_op { + struct scsi_qla_host *vha; + struct atio_from_isp atio; + uint8_t *purex_pyld; + uint16_t purex_pyld_len; + struct work_struct work; + struct list_head cmd_list; +}; + enum trace_flags { TRC_NEW_CMD = BIT_0, TRC_DO_WORK = BIT_1, @@ -1112,4 +1200,6 @@ void qlt_send_resp_ctio(struct qla_qpair *, struct qla_tgt_cmd *, uint8_t, extern void qlt_abort_cmd_on_host_reset(struct scsi_qla_host *, struct qla_tgt_cmd *); +/* 0 for FCP and 1 for NVMET */ +extern int qlt_op_target_mode; #endif /* __QLA_TARGET_H */ -- 2.12.0