On 5/31/21 9:05 AM, Nilesh Javali wrote: > From: Quinn Tran <qutran@xxxxxxxxxxx> > > Latest FC adapter from Marvell has the ability to encrypt > data in flight (EDIF) feature. This feature require an > application (ex: ipsec, etc) to act as an authenticator. > > This patch add ability for authentication application to > send and retrieve message(s) as part of the authentication > process via existing FC_BSG_HST_ELS_NOLOGIN BSG interface > > To send a message, application is expected to format the > data in the AUTH ELS format. Refer to FC-SP2 for details. > > If a message was received, application is required to reply with > either a LS_ACC or LS_RJT complete the exchange using the same > interface. Otherwise, remote device will treat it as a > timeout. > > Signed-off-by: Larry Wisneski <Larry.Wisneski@xxxxxxxxxxx> > Signed-off-by: Duane Grigsby <duane.grigsby@xxxxxxxxxxx> > Signed-off-by: Rick Hicksted Jr <rhicksted@xxxxxxxxxxx> > Signed-off-by: Quinn Tran <qutran@xxxxxxxxxxx> > Signed-off-by: Nilesh Javali <njavali@xxxxxxxxxxx> > --- > drivers/scsi/qla2xxx/qla_attr.c | 1 + > drivers/scsi/qla2xxx/qla_bsg.c | 63 +++-- > drivers/scsi/qla2xxx/qla_def.h | 48 ++++ > drivers/scsi/qla2xxx/qla_edif.c | 430 ++++++++++++++++++++++++++++++ > drivers/scsi/qla2xxx/qla_edif.h | 38 +++ > drivers/scsi/qla2xxx/qla_gbl.h | 6 +- > drivers/scsi/qla2xxx/qla_iocb.c | 42 +++ > drivers/scsi/qla2xxx/qla_isr.c | 95 ++++++- > drivers/scsi/qla2xxx/qla_os.c | 41 +++ > drivers/scsi/qla2xxx/qla_target.c | 7 +- > 10 files changed, 731 insertions(+), 40 deletions(-) > > diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c > index 3aa9869f6fae..d78db2949ef6 100644 > --- a/drivers/scsi/qla2xxx/qla_attr.c > +++ b/drivers/scsi/qla2xxx/qla_attr.c > @@ -3107,6 +3107,7 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) > qla2x00_wait_for_sess_deletion(vha); > > qla_nvme_delete(vha); > + qla_enode_stop(vha); > vha->flags.delete_progress = 1; > > qlt_remove_target(ha, vha); > diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c > index e6cccbcc7a1b..2d43603e31ec 100644 > --- a/drivers/scsi/qla2xxx/qla_bsg.c > +++ b/drivers/scsi/qla2xxx/qla_bsg.c > @@ -27,6 +27,10 @@ void qla2x00_bsg_job_done(srb_t *sp, int res) > > sp->free(sp); > > + ql_dbg(ql_dbg_user, sp->vha, 0x7009, > + "%s: sp hdl %x, result=%x bsg ptr %p\n", > + __func__, sp->handle, res, bsg_job); > + > bsg_reply->result = res; > bsg_job_done(bsg_job, bsg_reply->result, > bsg_reply->reply_payload_rcv_len); > @@ -53,11 +57,19 @@ void qla2x00_bsg_sp_free(srb_t *sp) > bsg_job->reply_payload.sg_list, > bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); > } else { > - dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, > - bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); > > - dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, > - bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); > + if (sp->remap.remapped) { > + dma_pool_free(ha->purex_dma_pool, sp->remap.rsp.buf, > + sp->remap.rsp.dma); > + dma_pool_free(ha->purex_dma_pool, sp->remap.req.buf, > + sp->remap.req.dma); > + } else { > + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, > + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); > + > + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, > + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); > + } > } > > if (sp->type == SRB_CT_CMD || > @@ -266,6 +278,7 @@ qla2x00_process_els(struct bsg_job *bsg_job) > int req_sg_cnt, rsp_sg_cnt; > int rval = (DID_ERROR << 16); > uint16_t nextlid = 0; > + uint32_t els_cmd = 0; > > if (bsg_request->msgcode == FC_BSG_RPT_ELS) { > rport = fc_bsg_to_rport(bsg_job); > @@ -279,6 +292,9 @@ qla2x00_process_els(struct bsg_job *bsg_job) > vha = shost_priv(host); > ha = vha->hw; > type = "FC_BSG_HST_ELS_NOLOGIN"; > + els_cmd = bsg_request->rqst_data.h_els.command_code; > + if (els_cmd == ELS_AUTH_ELS) > + return qla_edif_process_els(vha, bsg_job); > } > > if (!vha->flags.online) { > @@ -2948,27 +2964,26 @@ qla24xx_bsg_timeout(struct bsg_job *bsg_job) > > for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) { > sp = req->outstanding_cmds[cnt]; > - if (sp) { > - if (((sp->type == SRB_CT_CMD) || > - (sp->type == SRB_ELS_CMD_HST) || > - (sp->type == SRB_FXIOCB_BCMD)) > - && (sp->u.bsg_job == bsg_job)) { > - req->outstanding_cmds[cnt] = NULL; > - spin_unlock_irqrestore(&ha->hardware_lock, flags); > - if (ha->isp_ops->abort_command(sp)) { > - ql_log(ql_log_warn, vha, 0x7089, > - "mbx abort_command " > - "failed.\n"); > - bsg_reply->result = -EIO; > - } else { > - ql_dbg(ql_dbg_user, vha, 0x708a, > - "mbx abort_command " > - "success.\n"); > - bsg_reply->result = 0; > - } > - spin_lock_irqsave(&ha->hardware_lock, flags); > - goto done; > + if (sp && > + (sp->type == SRB_CT_CMD || > + sp->type == SRB_ELS_CMD_HST || > + sp->type == SRB_ELS_CMD_HST_NOLOGIN || > + sp->type == SRB_FXIOCB_BCMD) && > + sp->u.bsg_job == bsg_job) { > + req->outstanding_cmds[cnt] = NULL; > + spin_unlock_irqrestore(&ha->hardware_lock, flags); > + if (ha->isp_ops->abort_command(sp)) { > + ql_log(ql_log_warn, vha, 0x7089, > + "mbx abort_command failed.\n"); > + bsg_reply->result = -EIO; > + } else { > + ql_dbg(ql_dbg_user, vha, 0x708a, > + "mbx abort_command success.\n"); > + bsg_reply->result = 0; > } > + spin_lock_irqsave(&ha->hardware_lock, flags); > + goto done; > + > } > } > } > diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h > index 9c921381d020..517a4a4c178e 100644 > --- a/drivers/scsi/qla2xxx/qla_def.h > +++ b/drivers/scsi/qla2xxx/qla_def.h > @@ -341,6 +341,13 @@ struct name_list_extended { > u32 size; > u8 sent; > }; > + > +struct els_reject { > + struct fc_els_ls_rjt *c; > + dma_addr_t cdma; > + u16 size; > +}; > + > /* > * Timeout timer counts in seconds > */ > @@ -618,6 +625,21 @@ struct srb_iocb { > #define SRB_PRLI_CMD 21 > #define SRB_CTRL_VP 22 > #define SRB_PRLO_CMD 23 > +#define SRB_SA_UPDATE 25 > +#define SRB_ELS_CMD_HST_NOLOGIN 26 > +#define SRB_SA_REPLACE 27 > + > +struct qla_els_pt_arg { > + u8 els_opcode; > + u8 vp_idx; > + __le16 nport_handle; > + u16 control_flags; > + __le32 rx_xchg_address; > + port_id_t did; > + u32 tx_len, tx_byte_count, rx_len, rx_byte_count; > + dma_addr_t tx_addr, rx_addr; > + > +}; > > enum { > TYPE_SRB, > @@ -631,6 +653,13 @@ struct iocb_resource { > u16 iocb_cnt; > }; > > +struct bsg_cmd { > + struct bsg_job *bsg_job; > + union { > + struct qla_els_pt_arg els_arg; > + } u; > +}; > + > typedef struct srb { > /* > * Do not move cmd_type field, it needs to > @@ -663,7 +692,21 @@ typedef struct srb { > struct srb_iocb iocb_cmd; > struct bsg_job *bsg_job; > struct srb_cmd scmd; > + struct bsg_cmd bsg_cmd; > } u; > + struct { > + bool remapped; > + struct { > + dma_addr_t dma; > + void *buf; > + uint len; > + } req; > + struct { > + dma_addr_t dma; > + void *buf; > + uint len; > + } rsp; > + } remap; > /* > * Report completion status @res and call sp_put(@sp). @res is > * an NVMe status code, a SCSI result (e.g. DID_OK << 16) or a > @@ -4638,8 +4681,12 @@ struct qla_hw_data { > > struct qla_hw_data_stat stat; > pci_error_state_t pci_error_state; > + struct dma_pool *purex_dma_pool; > + struct els_reject elsrej; > }; > > +#define RX_ELS_SIZE (roundup(sizeof(struct enode) + ELS_MAX_PAYLOAD, SMP_CACHE_BYTES)) > + > struct active_regions { > uint8_t global; > struct { > @@ -5110,6 +5157,7 @@ enum nexus_wait_type { > WAIT_LUN, > }; > > +#define QLA_SKIP_HANDLE QLA_TGT_SKIP_HANDLE > /* Refer to SNIA SFF 8247 */ > struct sff_8247_a0 { > u8 txid; /* transceiver id */ > diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c > index fd39232fa68d..4c788f4588ca 100644 > --- a/drivers/scsi/qla2xxx/qla_edif.c > +++ b/drivers/scsi/qla2xxx/qla_edif.c > @@ -11,6 +11,30 @@ > #include <linux/delay.h> > #include <scsi/scsi_tcq.h> > > +static int qla_pur_get_pending(scsi_qla_host_t *, fc_port_t *, struct bsg_job *); > + > +static struct els_sub_cmd { > + uint16_t cmd; > + const char *str; > +} sc_str[] = { > + {SEND_ELS, "send ELS"}, > + {SEND_ELS_REPLY, "send ELS Reply"}, > + {PULL_ELS, "retrieve ELS"}, > +}; > + > +const char *sc_to_str(uint16_t cmd) > +{ > + int i; > + struct els_sub_cmd *e; > + > + for (i = 0; i < ARRAY_SIZE(sc_str); i++) { > + e = sc_str + i; > + if (cmd == e->cmd) > + return e->str; > + } > + return "unknown"; > +} > + > static void > qla_edif_sa_ctl_init(scsi_qla_host_t *vha, struct fc_port *fcport) > { > @@ -27,6 +51,73 @@ qla_edif_sa_ctl_init(scsi_qla_host_t *vha, struct fc_port *fcport) > fcport->edif.rx_bytes = 0; > } > > +static int qla_bsg_check(scsi_qla_host_t *vha, struct bsg_job *bsg_job, > +fc_port_t *fcport) > +{ > + struct extra_auth_els *p; > + struct fc_bsg_reply *bsg_reply = bsg_job->reply; > + struct qla_bsg_auth_els_request *req = > + (struct qla_bsg_auth_els_request *)bsg_job->request; > + > + if (!vha->hw->flags.edif_enabled) { > + /* edif support not enabled */ > + ql_dbg(ql_dbg_edif, vha, 0x9105, > + "%s edif not enabled\n", __func__); > + goto done; > + } > + if (vha->e_dbell.db_flags != EDB_ACTIVE) { > + /* doorbell list not enabled */ > + ql_dbg(ql_dbg_edif, vha, 0x09102, > + "%s doorbell not enabled\n", __func__); > + goto done; > + } > + > + p = &req->e; > + > + /* Get response */ > + if (p->sub_cmd == PULL_ELS) { > + struct qla_bsg_auth_els_reply *rpl = > + (struct qla_bsg_auth_els_reply *)bsg_job->reply; > + > + qla_pur_get_pending(vha, fcport, bsg_job); > + > + ql_dbg(ql_dbg_edif, vha, 0x911d, > + "%s %s %8phN sid=%x. xchg %x, nb=%xh bsg ptr %p\n", > + __func__, sc_to_str(p->sub_cmd), fcport->port_name, > + fcport->d_id.b24, rpl->rx_xchg_address, > + rpl->r.reply_payload_rcv_len, bsg_job); > + > + goto done; > + } > + return 0; > + > +done: > + > + bsg_job_done(bsg_job, bsg_reply->result, > + bsg_reply->reply_payload_rcv_len); > + return -EIO; > +} > + > +fc_port_t * > +qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id) > +{ > + fc_port_t *f, *tf; > + > + f = NULL; > + list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) { > + if ((f->flags & FCF_FCSP_DEVICE)) { > + ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x2058, > + "Found secure fcport - nn %8phN pn %8phN portid=%02x%02x%02x, 0x%x, 0x%x.\n", Indentation. > + f->node_name, f->port_name, > + f->d_id.b.domain, f->d_id.b.area, > + f->d_id.b.al_pa, f->d_id.b24, id->b24); > + if (f->d_id.b24 == id->b24) > + return f; > + } > + } > + return NULL; > +} > + > static int > qla_edif_app_check(scsi_qla_host_t *vha, struct app_id appid) > { > @@ -506,17 +597,192 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job) > > return rval; > } > + > +static void > +qla_enode_free(scsi_qla_host_t *vha, struct enode *node) > +{ > + /* > + * releases the space held by this enode entry > + * this function does _not_ free the enode itself > + * NB: the pur node entry passed should not be on any list > + */ If it shouldn't, why not add a WARN_ON(!list_empty()) or something here? > + > + if (!node) { > + ql_dbg(ql_dbg_edif, vha, 0x09122, > + "%s error - no valid node passed\n", __func__); > + return; > + } > + > + node->dinfo.lstate = LSTATE_DEST; > + node->ntype = N_UNDEF; > + kfree(node); > +} > + > +/* > + * function to initialize enode structs & lock > + * NB: should only be called when driver attaching > + */ > +void > +qla_enode_init(scsi_qla_host_t *vha) > +{ > + struct qla_hw_data *ha = vha->hw; > + char name[32]; > + > + if (vha->pur_cinfo.enode_flags == ENODE_ACTIVE) { > + /* list still active - error */ > + ql_dbg(ql_dbg_edif, vha, 0x09102, "%s enode still active\n", > + __func__); > + return; > + } > + > + /* initialize lock which protects pur_core & init list */ > + spin_lock_init(&vha->pur_cinfo.pur_lock); > + INIT_LIST_HEAD(&vha->pur_cinfo.head); > + > + snprintf(name, sizeof(name), "%s_%d_purex", QLA2XXX_DRIVER_NAME, > + ha->pdev->device); > +} > + > +/* > + * function to stop and clear and enode data > + * called when app notified it is stopping > + */ > + > void > qla_enode_stop(scsi_qla_host_t *vha) > { > + unsigned long flags; > + struct enode *node, *q; > + > if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) { > /* doorbell list not enabled */ > ql_dbg(ql_dbg_edif, vha, 0x09102, > "%s enode not active\n", __func__); > return; > } > + > + /* grab lock so list doesn't move */ > + spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); > + > + vha->pur_cinfo.enode_flags &= ~ENODE_ACTIVE; /* mark it not active */ > + > + /* hopefully this is a null list at this point */ > + list_for_each_entry_safe(node, q, &vha->pur_cinfo.head, list) { > + ql_dbg(ql_dbg_edif, vha, 0x910f, > + "%s freeing enode type=%x, cnt=%x\n", __func__, node->ntype, > + node->dinfo.nodecnt); > + list_del_init(&node->list); > + spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); > + qla_enode_free(vha, node); > + spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); The whole point of 'list_for_each_safe()' is that you don't need to protect against deletion of the entries. Having to drop the lock here will (slightly) defeat it's purpose. Also there's nothing in qla_enode_free() which would require the lock to be dropped. So please consider either not dropping the lock here or (maybe) move to implicit list unrolling like while (node = list_first_entry()) { list_del_init(); ... } > + } > + spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); > +} > + > +/* > + * allocate enode struct and populate buffer > + * returns: enode pointer with buffers > + * NULL on error > + */ > + > +static struct enode * > +qla_enode_find(scsi_qla_host_t *vha, uint32_t ntype, uint32_t p1, uint32_t p2) > +{ > + struct enode *node_rtn = NULL; > + struct enode *list_node = NULL; > + unsigned long flags; > + struct list_head *pos, *q; > + > + uint32_t sid; > + uint32_t rw_flag; > + > + struct purexevent *purex; > + > + /* secure the list from moving under us */ > + spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); > + > + list_for_each_safe(pos, q, &vha->pur_cinfo.head) { > + list_node = list_entry(pos, struct enode, list); list_for_each_entry() > + > + /* node type determines what p1 and p2 are */ > + purex = &list_node->u.purexinfo; > + sid = p1; > + rw_flag = p2; > + > + if (purex->pur_info.pur_sid.b24 == sid) { > + if (purex->pur_info.pur_pend == 1 && > + rw_flag == PUR_GET) { > + /* > + * if the receive is in progress > + * and its a read/get then can't > + * transfer yet > + */ > + ql_dbg(ql_dbg_edif, vha, 0x9106, > + "%s purex xfer in progress for sid=%x\n", > + __func__, sid); > + } else { > + /* found it and its complete */ > + node_rtn = list_node; > + } > + } > + > + if (node_rtn) { Why isn't the part of the 'else' branch above? > + /* > + * found node that we're looking for so take it > + * off the list and return it to the caller > + */ > + list_del(pos); > + list_node->dinfo.lstate = LSTATE_OFF; > + break; > + } > + } > + > + spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); > + > + return node_rtn; > } > > +/* > + * Return number of bytes of purex payload pending for consumption > + */ > +static int > +qla_pur_get_pending(scsi_qla_host_t *vha, fc_port_t *fcport, struct bsg_job *bsg_job) > +{ > + struct enode *ptr; > + struct purexevent *purex; > + struct qla_bsg_auth_els_reply *rpl = > + (struct qla_bsg_auth_els_reply *)bsg_job->reply; > + > + bsg_job->reply_len = sizeof(*rpl); > + > + ptr = qla_enode_find(vha, N_PUREX, fcport->d_id.b24, PUR_GET); > + if (!ptr) { > + ql_dbg(ql_dbg_edif, vha, 0x9111, > + "%s no enode data found for %8phN sid=%06x\n", > + __func__, fcport->port_name, fcport->d_id.b24); > + SET_DID_STATUS(rpl->r.result, DID_IMM_RETRY); > + return -EIO; > + } > + > + /* > + * enode is now off the linked list and is ours to deal with > + */ > + purex = &ptr->u.purexinfo; > + > + /* Copy info back to caller */ > + rpl->rx_xchg_address = purex->pur_info.pur_rx_xchg_address; > + > + SET_DID_STATUS(rpl->r.result, DID_OK); > + rpl->r.reply_payload_rcv_len = > + sg_pcopy_from_buffer(bsg_job->reply_payload.sg_list, > + bsg_job->reply_payload.sg_cnt, purex->msgp, > + purex->pur_info.pur_bytes_rcvd, 0); > + > + /* data copy / passback completed - destroy enode */ > + qla_enode_free(vha, ptr); > + > + return 0; > +} > /* function called when app is stopping */ > > void > @@ -529,3 +795,167 @@ qla_edb_stop(scsi_qla_host_t *vha) > return; > } > } > + > +static void qla_parse_auth_els_ctl(struct srb *sp) > +{ > + struct qla_els_pt_arg *a = &sp->u.bsg_cmd.u.els_arg; > + struct bsg_job *bsg_job = sp->u.bsg_cmd.bsg_job; > + struct fc_bsg_request *request = bsg_job->request; > + struct qla_bsg_auth_els_request *p = > + (struct qla_bsg_auth_els_request *)bsg_job->request; > + > + a->tx_len = a->tx_byte_count = sp->remap.req.len; > + a->tx_addr = sp->remap.req.dma; > + a->rx_len = a->rx_byte_count = sp->remap.rsp.len; > + a->rx_addr = sp->remap.rsp.dma; > + > + if (p->e.sub_cmd == SEND_ELS_REPLY) { > + a->control_flags = p->e.extra_control_flags << 13; > + a->rx_xchg_address = cpu_to_le32(p->e.extra_rx_xchg_address); > + if (p->e.extra_control_flags == BSG_CTL_FLAG_LS_ACC) > + a->els_opcode = ELS_LS_ACC; > + else if (p->e.extra_control_flags == BSG_CTL_FLAG_LS_RJT) > + a->els_opcode = ELS_LS_RJT; > + } > + a->did = sp->fcport->d_id; > + a->els_opcode = request->rqst_data.h_els.command_code; > + a->nport_handle = cpu_to_le16(sp->fcport->loop_id); > + a->vp_idx = sp->vha->vp_idx; > +} > + > +int qla_edif_process_els(scsi_qla_host_t *vha, struct bsg_job *bsg_job) > +{ > + struct fc_bsg_request *bsg_request = bsg_job->request; > + struct fc_bsg_reply *bsg_reply = bsg_job->reply; > + fc_port_t *fcport = NULL; > + struct qla_hw_data *ha = vha->hw; > + srb_t *sp; > + int rval = (DID_ERROR << 16); > + port_id_t d_id; > + struct qla_bsg_auth_els_request *p = > + (struct qla_bsg_auth_els_request *)bsg_job->request; > + > + d_id.b.al_pa = bsg_request->rqst_data.h_els.port_id[2]; > + d_id.b.area = bsg_request->rqst_data.h_els.port_id[1]; > + d_id.b.domain = bsg_request->rqst_data.h_els.port_id[0]; > + > + /* find matching d_id in fcport list */ > + fcport = qla2x00_find_fcport_by_pid(vha, &d_id); > + if (!fcport) { > + ql_dbg(ql_dbg_edif, vha, 0x911a, > + "%s fcport not find online portid=%06x.\n", > + __func__, d_id.b24); > + SET_DID_STATUS(bsg_reply->result, DID_ERROR); > + return -EIO; > + } > + > + if (qla_bsg_check(vha, bsg_job, fcport)) > + return 0; > + > + if (fcport->loop_id == FC_NO_LOOP_ID) { > + ql_dbg(ql_dbg_edif, vha, 0x910d, > + "%s ELS code %x, no loop id.\n", __func__, > + bsg_request->rqst_data.r_els.els_code); > + SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET); > + return -ENXIO; > + } > + > + if (!vha->flags.online) { > + ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n"); > + SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET); > + rval = -EIO; > + goto done; > + } > + > + /* pass through is supported only for ISP 4Gb or higher */ > + if (!IS_FWI2_CAPABLE(ha)) { > + ql_dbg(ql_dbg_user, vha, 0x7001, > + "ELS passthru not supported for ISP23xx based adapters.\n"); > + SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET); > + rval = -EPERM; > + goto done; > + } > + > + /* Alloc SRB structure */ > + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); > + if (!sp) { > + ql_dbg(ql_dbg_user, vha, 0x7004, > + "Failed get sp pid=%06x\n", fcport->d_id.b24); > + rval = -ENOMEM; > + SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); > + goto done; > + } > + > + sp->remap.req.len = bsg_job->request_payload.payload_len; > + sp->remap.req.buf = dma_pool_alloc(ha->purex_dma_pool, > + GFP_KERNEL, &sp->remap.req.dma); > + if (!sp->remap.req.buf) { > + ql_dbg(ql_dbg_user, vha, 0x7005, > + "Failed allocate request dma len=%x\n", > + bsg_job->request_payload.payload_len); > + rval = -ENOMEM; > + SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); > + goto done_free_sp; > + } > + > + sp->remap.rsp.len = bsg_job->reply_payload.payload_len; > + sp->remap.rsp.buf = dma_pool_alloc(ha->purex_dma_pool, > + GFP_KERNEL, &sp->remap.rsp.dma); > + if (!sp->remap.rsp.buf) { > + ql_dbg(ql_dbg_user, vha, 0x7006, > + "Failed allocate response dma len=%x\n", > + bsg_job->reply_payload.payload_len); > + rval = -ENOMEM; > + SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); > + goto done_free_remap_req; > + } > + /* > + * ql_print_bsg_sglist(ql_dbg_user, vha, 0x7008, > + * "SG bsg->request", &bsg_job->request_payload); > + */ ??? Debug code? Please move to 'dev_dbg' if you need it, otherwise delete it. > + sg_copy_to_buffer(bsg_job->request_payload.sg_list, > + bsg_job->request_payload.sg_cnt, sp->remap.req.buf, > + sp->remap.req.len); > + sp->remap.remapped = true; > + /* > + * ql_dump_buffer(ql_dbg_edif, vha, 0x70e0, > + * sp->remap.req.buf, bsg_job->request_payload.payload_len); > + */ Same here. > + > + sp->type = SRB_ELS_CMD_HST_NOLOGIN; > + sp->name = "SPCN_BSG_HST_NOLOGIN"; > + sp->u.bsg_cmd.bsg_job = bsg_job; > + qla_parse_auth_els_ctl(sp); > + > + sp->free = qla2x00_bsg_sp_free; > + sp->done = qla2x00_bsg_job_done; > + > + rval = qla2x00_start_sp(sp); > + > + ql_dbg(ql_dbg_edif, vha, 0x700a, > + "%s %s %8phN xchg %x ctlflag %x hdl %x reqlen %xh bsg ptr %p\n", > + __func__, sc_to_str(p->e.sub_cmd), fcport->port_name, > + p->e.extra_rx_xchg_address, p->e.extra_control_flags, > + sp->handle, sp->remap.req.len, bsg_job); > + > + if (rval != QLA_SUCCESS) { > + ql_log(ql_log_warn, vha, 0x700e, > + "qla2x00_start_sp failed = %d\n", rval); > + SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); > + rval = -EIO; > + goto done_free_remap_rsp; > + } > + return rval; > + > +done_free_remap_rsp: > + dma_pool_free(ha->purex_dma_pool, sp->remap.rsp.buf, > + sp->remap.rsp.dma); > +done_free_remap_req: > + dma_pool_free(ha->purex_dma_pool, sp->remap.req.buf, > + sp->remap.req.dma); > +done_free_sp: > + qla2x00_rel_sp(sp); > + > +done: > + return rval; > +} > diff --git a/drivers/scsi/qla2xxx/qla_edif.h b/drivers/scsi/qla2xxx/qla_edif.h > index dc0a08570a0b..12607218df17 100644 > --- a/drivers/scsi/qla2xxx/qla_edif.h > +++ b/drivers/scsi/qla2xxx/qla_edif.h > @@ -29,4 +29,42 @@ struct edif_dbell { > struct completion dbell; /* doorbell ring */ > }; > > +#define MAX_PAYLOAD 1024 > +#define PUR_GET 1 > + > +#define LSTATE_OFF 1 // node not on list > +#define LSTATE_ON 2 // node on list > +#define LSTATE_DEST 3 // node destoyed Why do you need this? If the node is on a list it's 'list_head' structure should be pointing to a list, ie it should be possible to do a 'list_empty()' to get this information, no? > + > +struct dinfo { > + int nodecnt; // create seq count > + int lstate; // node's list state > +}; > + > +struct pur_ninfo { > + unsigned int pur_pend:1; > + port_id_t pur_sid; > + port_id_t pur_did; > + uint8_t vp_idx; > + short pur_bytes_rcvd; > + unsigned short pur_nphdl; > + unsigned int pur_rx_xchg_address; > +}; > + > +struct purexevent { > + struct pur_ninfo pur_info; > + unsigned char *msgp; > + u32 msgp_len; > +}; > + > +#define N_UNDEF 0 // node not used/defined > +#define N_PUREX 1 // purex info > +struct enode { > + struct list_head list; > + struct dinfo dinfo; > + uint32_t ntype; > + union { > + struct purexevent purexinfo; > + } u; > +}; > #endif /* __QLA_EDIF_H */ > diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h > index 02c10caed18b..7ff05aa10b2d 100644 > --- a/drivers/scsi/qla2xxx/qla_gbl.h > +++ b/drivers/scsi/qla2xxx/qla_gbl.h > @@ -130,6 +130,8 @@ void qla24xx_free_purex_item(struct purex_item *item); > extern bool qla24xx_risc_firmware_invalid(uint32_t *); > void qla_init_iocb_limit(scsi_qla_host_t *); > > +int qla_edif_process_els(scsi_qla_host_t *vha, struct bsg_job *bsgjob); > +const char *sc_to_str(uint16_t cmd); > > /* > * Global Data in qla_os.c source file. > @@ -282,7 +284,8 @@ extern int qla2x00_vp_abort_isp(scsi_qla_host_t *); > /* > * Global Function Prototypes in qla_iocb.c source file. > */ > - > +void qla_els_pt_iocb(struct scsi_qla_host *vha, > + struct els_entry_24xx *pkt, struct qla_els_pt_arg *a); > extern uint16_t qla2x00_calc_iocbs_32(uint16_t); > extern uint16_t qla2x00_calc_iocbs_64(uint16_t); > extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t); > @@ -953,6 +956,7 @@ extern void qla_nvme_abort_process_comp_status > > /* nvme.c */ > void qla_nvme_unregister_remote_port(struct fc_port *fcport); > +fc_port_t *qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id); > void qla_edb_stop(scsi_qla_host_t *vha); > int32_t qla_edif_app_mgmt(struct bsg_job *bsg_job); > void qla_enode_init(scsi_qla_host_t *vha); > diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c > index 38b5bdde2405..6f996fb5e8f9 100644 > --- a/drivers/scsi/qla2xxx/qla_iocb.c > +++ b/drivers/scsi/qla2xxx/qla_iocb.c > @@ -3102,6 +3102,44 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode, > return rval; > } > > +/* it is assume qpair lock is held */ > +void qla_els_pt_iocb(struct scsi_qla_host *vha, > + struct els_entry_24xx *els_iocb, > + struct qla_els_pt_arg *a) > +{ > + els_iocb->entry_type = ELS_IOCB_TYPE; > + els_iocb->entry_count = 1; > + els_iocb->sys_define = 0; > + els_iocb->entry_status = 0; > + els_iocb->handle = QLA_SKIP_HANDLE; > + els_iocb->nport_handle = a->nport_handle; > + // no need for endiance conversion. No C++ comments, please. > + els_iocb->rx_xchg_address = a->rx_xchg_address; > + els_iocb->tx_dsd_count = cpu_to_le16(1); > + els_iocb->vp_index = a->vp_idx; > + els_iocb->sof_type = EST_SOFI3; > + els_iocb->rx_dsd_count = cpu_to_le16(0); > + els_iocb->opcode = a->els_opcode; > + > + els_iocb->d_id[0] = a->did.b.al_pa; > + els_iocb->d_id[1] = a->did.b.area; > + els_iocb->d_id[2] = a->did.b.domain; > + /* For SID the byte order is different than DID */ > + els_iocb->s_id[1] = vha->d_id.b.al_pa; > + els_iocb->s_id[2] = vha->d_id.b.area; > + els_iocb->s_id[0] = vha->d_id.b.domain; > + > + els_iocb->control_flags = cpu_to_le16(a->control_flags); > + > + els_iocb->tx_byte_count = cpu_to_le32(a->tx_byte_count); > + els_iocb->tx_len = cpu_to_le32(a->tx_len); > + put_unaligned_le64(a->tx_addr, &els_iocb->tx_address); > + > + els_iocb->rx_byte_count = cpu_to_le32(a->rx_byte_count); > + els_iocb->rx_len = cpu_to_le32(a->rx_len); > + put_unaligned_le64(a->rx_addr, &els_iocb->rx_address); > +} > + > static void > qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) > { > @@ -3800,6 +3838,10 @@ qla2x00_start_sp(srb_t *sp) > case SRB_ELS_CMD_HST: > qla24xx_els_iocb(sp, pkt); > break; > + case SRB_ELS_CMD_HST_NOLOGIN: > + qla_els_pt_iocb(sp->vha, pkt, &sp->u.bsg_cmd.u.els_arg); > + ((struct els_entry_24xx *)pkt)->handle = sp->handle; > + break; > case SRB_CT_CMD: > IS_FWI2_CAPABLE(ha) ? > qla24xx_ct_iocb(sp, pkt) : > diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c > index 6e8f737a4af3..a130a2db2cba 100644 > --- a/drivers/scsi/qla2xxx/qla_isr.c > +++ b/drivers/scsi/qla2xxx/qla_isr.c > @@ -1971,7 +1971,7 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, > } > > static void > -qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, > +qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, > struct sts_entry_24xx *pkt, int iocb_type) > { > struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt; > @@ -1982,18 +1982,58 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, > struct fc_bsg_reply *bsg_reply; > uint16_t comp_status; > uint32_t fw_status[3]; > - int res; > + int res, logit = 1; > struct srb_iocb *els; > + uint n; > + scsi_qla_host_t *vha; > + struct els_sts_entry_24xx *e = (struct els_sts_entry_24xx *)pkt; > > - sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); > + sp = qla2x00_get_sp_from_handle(v, func, req, pkt); > if (!sp) > return; > + bsg_job = sp->u.bsg_job; > + vha = sp->vha; > > type = NULL; > + > + comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status); > + fw_status[1] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_1); > + fw_status[2] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_2); > + > switch (sp->type) { > case SRB_ELS_CMD_RPT: > case SRB_ELS_CMD_HST: > + type = "rpt hst"; > + break; > + case SRB_ELS_CMD_HST_NOLOGIN: > type = "els"; > + { > + struct els_entry_24xx *els = (void *)pkt; > + struct qla_bsg_auth_els_request *p = > + (struct qla_bsg_auth_els_request *)bsg_job->request; > + > + ql_dbg(ql_dbg_user, vha, 0x700f, > + "%s %s. portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n", > + __func__, sc_to_str(p->e.sub_cmd), > + e->d_id[2], e->d_id[1], e->d_id[0], > + comp_status, p->e.extra_rx_xchg_address, bsg_job); > + > + if (!(le16_to_cpu(els->control_flags) & ECF_PAYLOAD_DESCR_MASK)) { > + if (sp->remap.remapped) { > + n = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, > + bsg_job->reply_payload.sg_cnt, > + sp->remap.rsp.buf, > + sp->remap.rsp.len); > + ql_dbg(ql_dbg_user + ql_dbg_verbose, vha, 0x700e, > + "%s: SG copied %x of %x\n", > + __func__, n, sp->remap.rsp.len); > + } else { > + ql_dbg(ql_dbg_user, vha, 0x700f, > + "%s: NOT REMAPPED (error)...!!!\n", > + __func__); > + } > + } > + } > break; > case SRB_CT_CMD: > type = "ct pass-through"; > @@ -2023,10 +2063,6 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, > return; > } > > - comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status); > - fw_status[1] = le32_to_cpu(ese->error_subcode_1); > - fw_status[2] = le32_to_cpu(ese->error_subcode_2); > - > if (iocb_type == ELS_IOCB_TYPE) { > els = &sp->u.iocb_cmd; > els->u.els_plogi.fw_status[0] = cpu_to_le32(fw_status[0]); > @@ -2040,15 +2076,52 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, > res = DID_OK << 16; > els->u.els_plogi.len = cpu_to_le16(le32_to_cpu( > ese->total_byte_count)); > + > + if (sp->remap.remapped && > + ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_ACC) { > + ql_dbg(ql_dbg_user, vha, 0x503f, > + "%s IOCB Done LS_ACC %02x%02x%02x -> %02x%02x%02x", > + __func__, e->s_id[0], e->s_id[2], e->s_id[1], > + e->d_id[2], e->d_id[1], e->d_id[0]); > + logit = 0; > + } > + > + } else if (comp_status == CS_PORT_LOGGED_OUT) { > + els->u.els_plogi.len = 0; > + res = DID_IMM_RETRY << 16; > } else { > els->u.els_plogi.len = 0; > res = DID_ERROR << 16; > } > + > + if (logit) { > + if (sp->remap.remapped && > + ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_RJT) { > + ql_dbg(ql_dbg_user, vha, 0x503f, > + "%s IOCB Done LS_RJT hdl=%x comp_status=0x%x\n", > + type, sp->handle, comp_status); > + > + ql_dbg(ql_dbg_user, vha, 0x503f, > + "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n", > + fw_status[1], fw_status[2], > + le32_to_cpu(((struct els_sts_entry_24xx *) > + pkt)->total_byte_count), > + e->s_id[0], e->s_id[2], e->s_id[1], > + e->d_id[2], e->d_id[1], e->d_id[0]); > + } else { > + ql_log(ql_log_info, vha, 0x503f, > + "%s IOCB Done hdl=%x comp_status=0x%x\n", > + type, sp->handle, comp_status); > + ql_log(ql_log_info, vha, 0x503f, > + "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n", > + fw_status[1], fw_status[2], > + le32_to_cpu(((struct els_sts_entry_24xx *) > + pkt)->total_byte_count), > + e->s_id[0], e->s_id[2], e->s_id[1], > + e->d_id[2], e->d_id[1], e->d_id[0]); > + } > + } //logit > } > - ql_dbg(ql_dbg_disc, vha, 0x503f, > - "ELS IOCB Done -%s hdl=%x comp_status=0x%x error subcode 1=0x%x error subcode 2=0x%x total_byte=0x%x\n", > - type, sp->handle, comp_status, fw_status[1], fw_status[2], > - le32_to_cpu(ese->total_byte_count)); > goto els_ct_done; > } > > diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c > index 4eab564ea6a0..5e39977af9ba 100644 > --- a/drivers/scsi/qla2xxx/qla_os.c > +++ b/drivers/scsi/qla2xxx/qla_os.c > @@ -3460,6 +3460,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) > return 0; > > probe_failed: > + qla_enode_stop(base_vha); > if (base_vha->gnl.l) { > dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size, > base_vha->gnl.l, base_vha->gnl.ldma); > @@ -3762,6 +3763,7 @@ qla2x00_remove_one(struct pci_dev *pdev) > base_vha->gnl.size, base_vha->gnl.l, base_vha->gnl.ldma); > > base_vha->gnl.l = NULL; > + qla_enode_stop(base_vha); > > vfree(base_vha->scan.l); > > @@ -4264,8 +4266,36 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, > goto fail_flt_buffer; > } > > + /* allocate the purex dma pool */ > + ha->purex_dma_pool = dma_pool_create(name, &ha->pdev->dev, > + MAX_PAYLOAD, 8, 0); > + > + if (!ha->purex_dma_pool) { > + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b, > + "Unable to allocate purex_dma_pool.\n"); > + goto fail_flt; > + } > + > + ha->elsrej.size = sizeof(struct fc_els_ls_rjt) + 16; > + ha->elsrej.c = dma_alloc_coherent(&ha->pdev->dev, > + ha->elsrej.size, &ha->elsrej.cdma, GFP_KERNEL); > + > + if (!ha->elsrej.c) { > + ql_dbg_pci(ql_dbg_init, ha->pdev, 0xffff, > + "Alloc failed for els reject cmd.\n"); > + goto fail_elsrej; > + } > + ha->elsrej.c->er_cmd = ELS_LS_RJT; > + ha->elsrej.c->er_reason = ELS_RJT_BUSY; > + ha->elsrej.c->er_explan = ELS_EXPL_UNAB_DATA; > return 0; > > +fail_elsrej: > + dma_pool_destroy(ha->purex_dma_pool); > +fail_flt: > + dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE, > + ha->flt, ha->flt_dma); > + > fail_flt_buffer: > dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE, > ha->sfp_data, ha->sfp_data_dma); > @@ -4776,6 +4806,16 @@ qla2x00_mem_free(struct qla_hw_data *ha) > if (ha->init_cb) > dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, > ha->init_cb, ha->init_cb_dma); > + > + dma_pool_destroy(ha->purex_dma_pool); > + ha->purex_dma_pool = NULL; > + > + if (ha->elsrej.c) { > + dma_free_coherent(&ha->pdev->dev, ha->elsrej.size, > + ha->elsrej.c, ha->elsrej.cdma); > + ha->elsrej.c = NULL; > + } > + > ha->init_cb = NULL; > ha->init_cb_dma = 0; > > @@ -4837,6 +4877,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, > spin_lock_init(&vha->cmd_list_lock); > init_waitqueue_head(&vha->fcport_waitQ); > init_waitqueue_head(&vha->vref_waitq); > + qla_enode_init(vha); > > vha->gnl.size = sizeof(struct get_name_list_extended) * > (ha->max_loop_id + 1); > diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c > index b2008fb1dd38..abf18b88579c 100644 > --- a/drivers/scsi/qla2xxx/qla_target.c > +++ b/drivers/scsi/qla2xxx/qla_target.c > @@ -184,8 +184,7 @@ static inline int qlt_issue_marker(struct scsi_qla_host *vha, int vha_locked) > return QLA_SUCCESS; > } > > -static inline > -struct scsi_qla_host *qlt_find_host_by_d_id(struct scsi_qla_host *vha, > +struct scsi_qla_host *qla_find_host_by_d_id(struct scsi_qla_host *vha, > be_id_t d_id) > { > struct scsi_qla_host *host; > @@ -299,7 +298,7 @@ static void qlt_try_to_dequeue_unknown_atios(struct scsi_qla_host *vha, > goto abort; > } > > - host = qlt_find_host_by_d_id(vha, u->atio.u.isp24.fcp_hdr.d_id); > + host = qla_find_host_by_d_id(vha, u->atio.u.isp24.fcp_hdr.d_id); > if (host != NULL) { > ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0x502f, > "Requeuing unknown ATIO_TYPE7 %p\n", u); > @@ -348,7 +347,7 @@ static bool qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, > switch (atio->u.raw.entry_type) { > case ATIO_TYPE7: > { > - struct scsi_qla_host *host = qlt_find_host_by_d_id(vha, > + struct scsi_qla_host *host = qla_find_host_by_d_id(vha, > atio->u.isp24.fcp_hdr.d_id); > if (unlikely(NULL == host)) { > ql_dbg(ql_dbg_tgt, vha, 0xe03e, > Cheers, Hannes -- Dr. Hannes Reinecke Kernel Storage Architect hare@xxxxxxx +49 911 74053 688 SUSE Software Solutions Germany GmbH, 90409 Nürnberg GF: F. Imendörffer, HRB 36809 (AG Nürnberg)