diff mbox series

[v2,03/10] qla2xxx: Add send, receive and accept for auth_els

Message ID 20210531070545.32072-4-njavali@marvell.com
State Superseded
Headers show
Series [v2,01/10] qla2xxx: Add start + stop bsg's | expand

Commit Message

Nilesh Javali May 31, 2021, 7:05 a.m. UTC
From: Quinn Tran <qutran@marvell.com>

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@marvell.com>
Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com>
Signed-off-by: Rick Hicksted Jr <rhicksted@marvell.com>
Signed-off-by: Quinn Tran <qutran@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 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(-)

Comments

Hannes Reinecke June 1, 2021, 12:47 p.m. UTC | #1
On 5/31/21 9:05 AM, Nilesh Javali wrote:
> From: Quinn Tran <qutran@marvell.com>

> 

> 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@marvell.com>

> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com>

> Signed-off-by: Rick Hicksted Jr <rhicksted@marvell.com>

> Signed-off-by: Quinn Tran <qutran@marvell.com>

> Signed-off-by: Nilesh Javali <njavali@marvell.com>

> ---

>  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@suse.de			               +49 911 74053 688
SUSE Software Solutions Germany GmbH, 90409 Nürnberg
GF: F. Imendörffer, HRB 36809 (AG Nürnberg)
Himanshu Madhani June 3, 2021, 8:43 p.m. UTC | #2
On 5/31/21 2:05 AM, Nilesh Javali wrote:
> From: Quinn Tran <qutran@marvell.com>

> 

> 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@marvell.com>

> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com>

> Signed-off-by: Rick Hicksted Jr <rhicksted@marvell.com>

> Signed-off-by: Quinn Tran <qutran@marvell.com>

> Signed-off-by: Nilesh Javali <njavali@marvell.com>

> ---

>   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",


fix 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 (!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

> + */


use kernel-doc style for description

> +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

> + */

> +


use kernel-doc style here for description

>   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);

> +	}

> +	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags);

> +}


Ah I see that the actual action is added here for enode_stop. maybe 
Patch 1 should have comment to that effect. (hard to follow patch series 
without context)

> +

> +/*

> + *  allocate enode struct and populate buffer

> + *  returns: enode pointer with buffers

> + *           NULL on error

> + */


Use kernel-doc style for description.

> +

> +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;

> +


extra line not necessary

> +	uint32_t		sid;

> +	uint32_t		rw_flag;

> +


extra line not necessary

> +	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);

> +

> +		/* 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) {

> +			/*

> +			 * 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);

> +	 */


remove debug prints or move message to debug bits that gets printed only 
during debug options.

> +	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);

> +	 */

> +


ditto here ... remove debug prints or move it debug bits which can be 
useful while debugging.

> +	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

> +


fix comments

> +struct dinfo {

> +	int		nodecnt;	// create seq count

> +	int		lstate;		// node's list state

> +};

> +


fix comments


> +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


fix comments

> +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.


fix or remove extra comment

> +	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]);


above message can be optimized.

> +				} 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]);


same here.. message should be optimized to use same print type as portid

> +				}

> +			} //logit


unnecessary comment, remove it or fix it

>   		}

> -		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,

> 


-- 
Himanshu Madhani                                Oracle Linux Engineering
Quinn Tran June 4, 2021, 7:41 p.m. UTC | #3
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;

> +	}

QT:   noted.  Will fix in v3.

> +		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();
  ...
}

> +	list_for_each_safe(pos, q, &vha->pur_cinfo.head) {

> +		list_node = list_entry(pos, struct enode, list);


list_for_each_entry()


QT:  Noted. We're still harden the code.  Will clean this up as part of next phase.

> +

> +		if (node_rtn) {


Why isn't the part of the 'else' branch above?

QT: will fix it in v3.

> +	/*

> +	 * 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.

QT: ack to various debug code comments.  will delete.


> +#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?

QT: I miss this as part of pruning. 
-----

> +	// no need for endiance conversion.


No C++ comments, please.

QT:  ack. Will fix in v3.


Regards,
Quinn Tran
diff mbox series

Patch

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",
+			    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 (!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);
+	}
+	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);
+
+		/* 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) {
+			/*
+			 * 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);
+	 */
+	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);
+	 */
+
+	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
+
+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.
+	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,