diff mbox

[v2,4/5] qla2xxx: Changes to support N2N logins

Message ID 20171013163407.8038-5-himanshu.madhani@cavium.com (mailing list archive)
State Accepted
Headers show

Commit Message

Madhani, Himanshu Oct. 13, 2017, 4:34 p.m. UTC
From: Duane Grigsby <duane.grigsby@cavium.com>

If we discovered a topology that is N2N then we will issue a login
to the target. If our WWPN is bigger than the target's WWPN then we
will initiate login, otherwise we will just wait for the target
to initiate login.

Signed-off-by: Duane Grigsby <duane.grigsby@cavium.com>
Signed-off-by: Michael Hernandez <michael.hernandez@cavium.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@cavium.com>
---
 drivers/scsi/qla2xxx/qla_def.h  |  24 +++++
 drivers/scsi/qla2xxx/qla_fw.h   |   4 +-
 drivers/scsi/qla2xxx/qla_gbl.h  |   4 +
 drivers/scsi/qla2xxx/qla_init.c | 135 +++++++++++++++++++++++++++-
 drivers/scsi/qla2xxx/qla_iocb.c | 195 +++++++++++++++++++++++++++++++++++++---
 drivers/scsi/qla2xxx/qla_isr.c  |  42 ++++++++-
 drivers/scsi/qla2xxx/qla_mbx.c  |  76 ++++++++++++++++
 7 files changed, 459 insertions(+), 21 deletions(-)

Comments

Ewan Milne Oct. 13, 2017, 5:01 p.m. UTC | #1
On Fri, 2017-10-13 at 09:34 -0700, Madhani, Madhani wrote:
> From: Duane Grigsby <duane.grigsby@cavium.com>
> 
> If we discovered a topology that is N2N then we will issue a login
> to the target. If our WWPN is bigger than the target's WWPN then we
> will initiate login, otherwise we will just wait for the target
> to initiate login.
> 
> Signed-off-by: Duane Grigsby <duane.grigsby@cavium.com>
> Signed-off-by: Michael Hernandez <michael.hernandez@cavium.com>
> Signed-off-by: Himanshu Madhani <himanshu.madhani@cavium.com>
> ---
>  drivers/scsi/qla2xxx/qla_def.h  |  24 +++++
>  drivers/scsi/qla2xxx/qla_fw.h   |   4 +-
>  drivers/scsi/qla2xxx/qla_gbl.h  |   4 +
>  drivers/scsi/qla2xxx/qla_init.c | 135 +++++++++++++++++++++++++++-
>  drivers/scsi/qla2xxx/qla_iocb.c | 195 +++++++++++++++++++++++++++++++++++++---
>  drivers/scsi/qla2xxx/qla_isr.c  |  42 ++++++++-
>  drivers/scsi/qla2xxx/qla_mbx.c  |  76 ++++++++++++++++
>  7 files changed, 459 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
> index f712c0cd46d6..01a9b8971e88 100644
> --- a/drivers/scsi/qla2xxx/qla_def.h
> +++ b/drivers/scsi/qla2xxx/qla_def.h
> @@ -323,6 +323,12 @@ struct els_logo_payload {
>  	uint8_t wwpn[WWN_SIZE];
>  };
>  
> +struct els_plogi_payload {
> +	uint8_t opcode;
> +	uint8_t rsvd[3];
> +	uint8_t data[112];
> +};
> +
>  struct ct_arg {
>  	void		*iocb;
>  	u16		nport_handle;
> @@ -358,6 +364,19 @@ struct srb_iocb {
>  			dma_addr_t els_logo_pyld_dma;
>  		} els_logo;
>  		struct {
> +#define ELS_DCMD_PLOGI 0x3
> +			uint32_t flags;
> +			uint32_t els_cmd;
> +			struct completion comp;
> +			struct els_plogi_payload *els_plogi_pyld;
> +			struct els_plogi_payload *els_resp_pyld;
> +			dma_addr_t els_plogi_pyld_dma;
> +			dma_addr_t els_resp_pyld_dma;
> +			uint32_t	fw_status[3];
> +			__le16	comp_status;
> +			__le16	len;
> +		} els_plogi;
> +		struct {
>  			/*
>  			 * Values for flags field below are as
>  			 * defined in tsk_mgmt_entry struct
> @@ -2349,6 +2368,7 @@ typedef struct fc_port {
>  	uint8_t fc4_type;
>  	uint8_t	fc4f_nvme;
>  	uint8_t scan_state;
> +	uint8_t n2n_flag;
>  
>  	unsigned long last_queue_full;
>  	unsigned long last_ramp_up;
> @@ -2372,6 +2392,7 @@ typedef struct fc_port {
>  	u8 iocb[IOCB_SIZE];
>  	u8 current_login_state;
>  	u8 last_login_state;
> +	struct completion n2n_done;
>  } fc_port_t;
>  
>  #define QLA_FCPORT_SCAN		1
> @@ -4228,6 +4249,9 @@ typedef struct scsi_qla_host {
>  	wait_queue_head_t fcport_waitQ;
>  	wait_queue_head_t vref_waitq;
>  	uint8_t min_link_speed_feat;
> +	uint8_t n2n_node_name[WWN_SIZE];
> +	uint8_t n2n_port_name[WWN_SIZE];
> +	uint16_t	n2n_id;
>  } scsi_qla_host_t;
>  
>  struct qla27xx_image_status {
> diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
> index bec641aae7b3..d5cef0727e72 100644
> --- a/drivers/scsi/qla2xxx/qla_fw.h
> +++ b/drivers/scsi/qla2xxx/qla_fw.h
> @@ -753,9 +753,7 @@ struct els_entry_24xx {
>  	uint8_t reserved_2;
>  
>  	uint8_t port_id[3];
> -	uint8_t reserved_3;
> -
> -	uint16_t reserved_4;
> +	uint8_t s_id[3];
>  
>  	uint16_t control_flags;		/* Control flags. */
>  #define ECF_PAYLOAD_DESCR_MASK	(BIT_15|BIT_14|BIT_13)
> diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
> index 46c7822c20fc..1e35e961683f 100644
> --- a/drivers/scsi/qla2xxx/qla_gbl.h
> +++ b/drivers/scsi/qla2xxx/qla_gbl.h
> @@ -45,6 +45,8 @@ extern int qla2x00_fabric_login(scsi_qla_host_t *, fc_port_t *, uint16_t *);
>  extern int qla2x00_local_device_login(scsi_qla_host_t *, fc_port_t *);
>  
>  extern int qla24xx_els_dcmd_iocb(scsi_qla_host_t *, int, port_id_t);
> +extern int qla24xx_els_dcmd2_iocb(scsi_qla_host_t *, int, fc_port_t *,
> +    port_id_t);
>  
>  extern void qla2x00_update_fcports(scsi_qla_host_t *);
>  
> @@ -487,6 +489,8 @@ int qla24xx_gidlist_wait(struct scsi_qla_host *, void *, dma_addr_t,
>      uint16_t *);
>  int __qla24xx_parse_gpdb(struct scsi_qla_host *, fc_port_t *,
>  	struct port_database_24xx *);
> +int qla24xx_get_port_login_templ(scsi_qla_host_t *, dma_addr_t,
> +    void *, uint16_t);
>  
>  extern int qla27xx_get_zio_threshold(scsi_qla_host_t *, uint16_t *);
>  extern int qla27xx_set_zio_threshold(scsi_qla_host_t *, uint16_t);
> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index 4c53199db371..b2a391f93775 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
> @@ -1433,6 +1433,14 @@ qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea)
>  		qla24xx_post_gpdb_work(vha, ea->fcport, 0);
>  		break;
>  	default:
> +		if (ea->fcport->n2n_flag) {
> +			ql_dbg(ql_dbg_disc, vha, 0x2118,
> +				"%s %d %8phC post fc4 prli\n",
> +				__func__, __LINE__, ea->fcport->port_name);
> +			ea->fcport->fc4f_nvme = 0;
> +			ea->fcport->n2n_flag = 0;
> +			qla24xx_post_prli_work(vha, ea->fcport);
> +		}
>  		ql_dbg(ql_dbg_disc, vha, 0x2119,
>  		    "%s %d %8phC unhandle event of %x\n",
>  		    __func__, __LINE__, ea->fcport->port_name, ea->data[0]);
> @@ -4366,7 +4374,109 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
>  	return (rval);
>  }
>  
> +/*
> + * N2N Login
> + *	Updates Fibre Channel Device Database with local loop devices.
> + *
> + * Input:
> + *	ha = adapter block pointer.
> + *
> + * Returns:
> + */
> +static int qla24xx_n2n_handle_login(struct scsi_qla_host *vha,
> +    fc_port_t *fcport)
> +{
> +	struct qla_hw_data *ha = vha->hw;
> +	int	res = QLA_SUCCESS, rval;
> +	int	greater_wwpn = 0;
> +	int	logged_in = 0;
> +
> +	if (ha->current_topology != ISP_CFG_N)
> +		return res;
> +
> +	if (wwn_to_u64(vha->port_name) >
> +	    wwn_to_u64(vha->n2n_port_name)) {
> +		ql_dbg(ql_dbg_disc, vha, 0x2002,
> +		    "HBA WWPN is greater %llx > target %llx\n",
> +		    wwn_to_u64(vha->port_name),
> +		    wwn_to_u64(vha->n2n_port_name));
> +		greater_wwpn = 1;
> +		fcport->d_id.b24 = vha->n2n_id;
> +	}
> +
> +	fcport->loop_id = vha->loop_id;
> +	fcport->fc4f_nvme = 0;
> +	fcport->query = 1;
> +
> +	ql_dbg(ql_dbg_disc, vha, 0x4001,
> +	    "Initiate N2N login handler: HBA port_id=%06x loopid=%d\n",
> +	    fcport->d_id.b24, vha->loop_id);
> +
> +	/* Fill in member data. */
> +	if (!greater_wwpn) {
> +		rval = qla2x00_get_port_database(vha, fcport, 0);
> +		ql_dbg(ql_dbg_disc, vha, 0x1051,
> +		    "Remote login-state (%x/%x) port_id=%06x loop_id=%x, rval=%d\n",
> +		    fcport->current_login_state, fcport->last_login_state,
> +		    fcport->d_id.b24, fcport->loop_id, rval);
> +
> +		if (((fcport->current_login_state & 0xf) == 0x4) ||
> +		    ((fcport->current_login_state & 0xf) == 0x6))
> +			logged_in = 1;
> +	}
> +
> +	if (logged_in || greater_wwpn) {
> +		if (!vha->nvme_local_port && vha->flags.nvme_enabled)
> +			qla_nvme_register_hba(vha);
> +
> +		/* Set connected N_Port d_id */
> +		if (vha->flags.nvme_enabled)
> +			fcport->fc4f_nvme = 1;
> +
> +		fcport->scan_state = QLA_FCPORT_FOUND;
> +		fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
> +		fcport->disc_state = DSC_GNL;
> +		fcport->n2n_flag = 1;
> +		fcport->flags = 3;
> +		vha->hw->flags.gpsc_supported = 0;
> +
> +		if (greater_wwpn) {
> +			ql_dbg(ql_dbg_disc, vha, 0x20e5,
> +			    "%s %d PLOGI ELS %8phC\n",
> +			    __func__, __LINE__, fcport->port_name);
> +
> +			res = qla24xx_els_dcmd2_iocb(vha, ELS_DCMD_PLOGI,
> +			    fcport, fcport->d_id);
> +		}
> +
> +		if (res != QLA_SUCCESS) {
> +			ql_log(ql_log_info, vha, 0xd04d,
> +			    "PLOGI Failed: portid=%06x - retrying\n",
> +			    fcport->d_id.b24);
> +			res = QLA_SUCCESS;
> +		} else {
> +			/* State 0x6 means FCP PRLI complete */
> +			if ((fcport->current_login_state & 0xf) == 0x6) {
> +				ql_dbg(ql_dbg_disc, vha, 0x2118,
> +				    "%s %d %8phC post GPDB work\n",
> +				    __func__, __LINE__, fcport->port_name);
> +				fcport->chip_reset =
> +				    vha->hw->base_qpair->chip_reset;
> +				qla24xx_post_gpdb_work(vha, fcport, 0);
> +			} else {
> +				ql_dbg(ql_dbg_disc, vha, 0x2118,
> +				    "%s %d %8phC post NVMe PRLI\n",
> +				    __func__, __LINE__, fcport->port_name);
> +				qla24xx_post_prli_work(vha, fcport);
> +			}
> +		}
> +	} else {
> +		/* Wait for next database change */
> +		set_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags);
> +	}
>  
> +	return res;
> +}
>  
>  /*
>   * qla2x00_configure_local_loop
> @@ -4437,6 +4547,14 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
>  		}
>  	}
>  
> +	/* Inititae N2N login. */
> +	if (test_and_clear_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags)) {
> +		rval = qla24xx_n2n_handle_login(vha, new_fcport);
> +		if (rval != QLA_SUCCESS)
> +			goto cleanup_allocation;
> +		return QLA_SUCCESS;
> +	}
> +
>  	/* Add devices to port list. */
>  	id_iter = (char *)ha->gid_list;
>  	for (index = 0; index < entries; index++) {
> @@ -4478,10 +4596,13 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
>  			    "Failed to retrieve fcport information "
>  			    "-- get_port_database=%x, loop_id=0x%04x.\n",
>  			    rval2, new_fcport->loop_id);
> -			ql_dbg(ql_dbg_disc, vha, 0x2105,
> -			    "Scheduling resync.\n");
> -			set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
> -			continue;
> +			/* Skip retry if N2N */
> +			if (ha->current_topology != ISP_CFG_N) {
> +				ql_dbg(ql_dbg_disc, vha, 0x2105,
> +				    "Scheduling resync.\n");
> +				set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
> +				continue;
> +			}
>  		}
>  
>  		spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
> @@ -7554,6 +7675,12 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
>  	if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha))
>  		icb->firmware_options_3 |= BIT_0;
>  
> +	if (IS_QLA27XX(ha)) {
> +		icb->firmware_options_3 |= BIT_8;
> +		ql_dbg(ql_log_info, vha, 0x0075,
> +		    "Enabling direct connection.\n");
> +	}
> +
>  	if (rval) {
>  		ql_log(ql_log_warn, vha, 0x0076,
>  		    "NVRAM configuration failed.\n");
> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> index 2f94159186d7..0502ed7ee14e 100644
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c
> @@ -2518,6 +2518,7 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
>  {
>  	scsi_qla_host_t *vha = sp->vha;
>  	struct srb_iocb *elsio = &sp->u.iocb_cmd;
> +	uint32_t	dsd_len = 24;
>  
>  	els_iocb->entry_type = ELS_IOCB_TYPE;
>  	els_iocb->entry_count = 1;
> @@ -2534,24 +2535,198 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
>  	els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
>  	els_iocb->port_id[1] = sp->fcport->d_id.b.area;
>  	els_iocb->port_id[2] = sp->fcport->d_id.b.domain;
> +	els_iocb->s_id[0] = vha->d_id.b.al_pa;
> +	els_iocb->s_id[1] = vha->d_id.b.area;
> +	els_iocb->s_id[2] = vha->d_id.b.domain;
>  	els_iocb->control_flags = 0;
>  
> -	els_iocb->tx_byte_count = sizeof(struct els_logo_payload);
> -	els_iocb->tx_address[0] =
> -	    cpu_to_le32(LSD(elsio->u.els_logo.els_logo_pyld_dma));
> -	els_iocb->tx_address[1] =
> -	    cpu_to_le32(MSD(elsio->u.els_logo.els_logo_pyld_dma));
> -	els_iocb->tx_len = cpu_to_le32(sizeof(struct els_logo_payload));
> +	if (elsio->u.els_logo.els_cmd == ELS_DCMD_PLOGI) {
> +		els_iocb->tx_byte_count = sizeof(struct els_plogi_payload);
> +		els_iocb->tx_address[0] =
> +			cpu_to_le32(LSD(elsio->u.els_plogi.els_plogi_pyld_dma));
> +		els_iocb->tx_address[1] =
> +			cpu_to_le32(MSD(elsio->u.els_plogi.els_plogi_pyld_dma));
> +		els_iocb->tx_len = dsd_len;
> +
> +		els_iocb->rx_dsd_count = 1;
> +		els_iocb->rx_byte_count = sizeof(struct els_plogi_payload);
> +		els_iocb->rx_address[0] =
> +			cpu_to_le32(LSD(elsio->u.els_plogi.els_resp_pyld_dma));
> +		els_iocb->rx_address[1] =
> +			cpu_to_le32(MSD(elsio->u.els_plogi.els_resp_pyld_dma));
> +		els_iocb->rx_len = dsd_len;
> +		ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3073,
> +		    "PLOGI ELS IOCB:\n");
> +		ql_dump_buffer(ql_log_info, vha, 0x0109,
> +		    (uint8_t *)els_iocb, 0x70);
> +	} else {
> +		els_iocb->tx_byte_count = sizeof(struct els_logo_payload);
> +		els_iocb->tx_address[0] =
> +		    cpu_to_le32(LSD(elsio->u.els_logo.els_logo_pyld_dma));
> +		els_iocb->tx_address[1] =
> +		    cpu_to_le32(MSD(elsio->u.els_logo.els_logo_pyld_dma));
> +		els_iocb->tx_len = cpu_to_le32(sizeof(struct els_logo_payload));
>  
> -	els_iocb->rx_byte_count = 0;
> -	els_iocb->rx_address[0] = 0;
> -	els_iocb->rx_address[1] = 0;
> -	els_iocb->rx_len = 0;
> +		els_iocb->rx_byte_count = 0;
> +		els_iocb->rx_address[0] = 0;
> +		els_iocb->rx_address[1] = 0;
> +		els_iocb->rx_len = 0;
> +	}
>  
>  	sp->vha->qla_stats.control_requests++;
>  }
>  
>  static void
> +qla2x00_els_dcmd2_sp_free(void *data)
> +{
> +	srb_t *sp = data;
> +	struct srb_iocb *elsio = &sp->u.iocb_cmd;
> +
> +	if (elsio->u.els_plogi.els_plogi_pyld)
> +		dma_free_coherent(&sp->vha->hw->pdev->dev, DMA_POOL_SIZE,
> +		    elsio->u.els_plogi.els_plogi_pyld,
> +		    elsio->u.els_plogi.els_plogi_pyld_dma);
> +
> +	if (elsio->u.els_plogi.els_resp_pyld)
> +		dma_free_coherent(&sp->vha->hw->pdev->dev, DMA_POOL_SIZE,
> +		    elsio->u.els_plogi.els_resp_pyld,
> +		    elsio->u.els_plogi.els_resp_pyld_dma);
> +
> +	del_timer(&elsio->timer);
> +	qla2x00_rel_sp(sp);
> +}
> +
> +static void
> +qla2x00_els_dcmd2_iocb_timeout(void *data)
> +{
> +	srb_t *sp = data;
> +	fc_port_t *fcport = sp->fcport;
> +	struct scsi_qla_host *vha = sp->vha;
> +	struct qla_hw_data *ha = vha->hw;
> +	struct srb_iocb *lio = &sp->u.iocb_cmd;
> +	unsigned long flags = 0;
> +	int res;
> +
> +	ql_dbg(ql_dbg_io + ql_dbg_disc, vha, 0x3069,
> +	    "%s hdl=%x ELS Timeout, %8phC portid=%06x\n",
> +	    sp->name, sp->handle, fcport->port_name, fcport->d_id.b24);
> +
> +	/* Abort the exchange */
> +	spin_lock_irqsave(&ha->hardware_lock, flags);
> +	res = ha->isp_ops->abort_command(sp);
> +	ql_dbg(ql_dbg_io, vha, 0x3070,
> +	    "mbx abort_command %s\n",
> +	    (res == QLA_SUCCESS) ? "successful" : "failed");
> +	spin_unlock_irqrestore(&ha->hardware_lock, flags);
> +
> +	complete(&lio->u.els_plogi.comp);
> +}
> +
> +static void
> +qla2x00_els_dcmd2_sp_done(void *ptr, int res)
> +{
> +	srb_t *sp = ptr;
> +	fc_port_t *fcport = sp->fcport;
> +	struct srb_iocb *lio = &sp->u.iocb_cmd;
> +	struct scsi_qla_host *vha = sp->vha;
> +
> +	ql_dbg(ql_dbg_io + ql_dbg_disc, vha, 0x3072,
> +	    "%s ELS hdl=%x, portid=%06x done %8pC\n",
> +	    sp->name, sp->handle, fcport->d_id.b24, fcport->port_name);
> +
> +	complete(&lio->u.els_plogi.comp);
> +}
> +
> +int
> +qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode,
> +    fc_port_t *fcport, port_id_t remote_did)
> +{
> +	srb_t *sp;
> +	struct srb_iocb *elsio = NULL;
> +	struct qla_hw_data *ha = vha->hw;
> +	int rval = QLA_SUCCESS;
> +	void	*ptr, *resp_ptr;
> +	dma_addr_t ptr_dma;
> +
> +	/* Alloc SRB structure */
> +	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
> +	if (!sp) {
> +		ql_log(ql_log_info, vha, 0x70e6,
> +		 "SRB allocation failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	elsio = &sp->u.iocb_cmd;
> +	fcport->d_id.b.domain = remote_did.b.domain;
> +	fcport->d_id.b.area = remote_did.b.area;
> +	fcport->d_id.b.al_pa = remote_did.b.al_pa;
> +
> +	ql_dbg(ql_dbg_io, vha, 0x3073,
> +	    "Enter: PLOGI portid=%06x\n", fcport->d_id.b24);
> +
> +	sp->type = SRB_ELS_DCMD;
> +	sp->name = "ELS_DCMD";
> +	sp->fcport = fcport;
> +	qla2x00_init_timer(sp, ELS_DCMD_TIMEOUT);
> +	elsio->timeout = qla2x00_els_dcmd2_iocb_timeout;
> +	sp->done = qla2x00_els_dcmd2_sp_done;
> +	sp->free = qla2x00_els_dcmd2_sp_free;
> +
> +	ptr = elsio->u.els_plogi.els_plogi_pyld =
> +	    dma_alloc_coherent(&ha->pdev->dev, DMA_POOL_SIZE,
> +		&elsio->u.els_plogi.els_plogi_pyld_dma, GFP_KERNEL);
> +	ptr_dma = elsio->u.els_plogi.els_plogi_pyld_dma;
> +
> +	if (!elsio->u.els_plogi.els_plogi_pyld) {
> +		rval = QLA_FUNCTION_FAILED;
> +		goto out;
> +	}
> +
> +	resp_ptr = elsio->u.els_plogi.els_resp_pyld =
> +	    dma_alloc_coherent(&ha->pdev->dev, DMA_POOL_SIZE,
> +		&elsio->u.els_plogi.els_resp_pyld_dma, GFP_KERNEL);
> +
> +	if (!elsio->u.els_plogi.els_resp_pyld) {
> +		rval = QLA_FUNCTION_FAILED;
> +		goto out;
> +	}
> +
> +	ql_dbg(ql_dbg_io, vha, 0x3073, "PLOGI %p %p\n", ptr, resp_ptr);
> +
> +	memset(ptr, 0, sizeof(struct els_plogi_payload));
> +	memset(resp_ptr, 0, sizeof(struct els_plogi_payload));
> +	elsio->u.els_plogi.els_cmd = els_opcode;
> +	elsio->u.els_plogi.els_plogi_pyld->opcode = els_opcode;
> +	qla24xx_get_port_login_templ(vha, ptr_dma + 4,
> +		&elsio->u.els_plogi.els_plogi_pyld->data[0],
> +		sizeof(struct els_plogi_payload));
> +
> +	ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3073, "PLOGI buffer:\n");
> +	ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x0109,
> +	    (uint8_t *)elsio->u.els_plogi.els_plogi_pyld, 0x70);
> +
> +	init_completion(&elsio->u.els_plogi.comp);
> +	rval = qla2x00_start_sp(sp);
> +	if (rval != QLA_SUCCESS) {
> +		rval = QLA_FUNCTION_FAILED;
> +		goto out;
> +	}
> +
> +	ql_dbg(ql_dbg_io, vha, 0x3074,
> +	    "%s PLOGI sent, hdl=%x, loopid=%x, portid=%06x\n",
> +	    sp->name, sp->handle, fcport->loop_id, fcport->d_id.b24);
> +
> +	wait_for_completion(&elsio->u.els_plogi.comp);
> +
> +	if (elsio->u.els_plogi.comp_status != CS_COMPLETE)
> +		rval = QLA_FUNCTION_FAILED;
> +
> +out:
> +	sp->free(sp);
> +	return rval;
> +}
> +
> +static void
>  qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
>  {
>  	struct bsg_job *bsg_job = sp->u.bsg_job;
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index ab97fb06c239..d06a1a809188 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -1041,6 +1041,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
>  		 */
>  		atomic_set(&vha->loop_down_timer, 0);
>  		if (atomic_read(&vha->loop_state) != LOOP_DOWN &&
> +			!ha->flags.n2n_ae  &&
>  		    atomic_read(&vha->loop_state) != LOOP_DEAD) {
>  			ql_dbg(ql_dbg_async, vha, 0x5011,
>  			    "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n",
> @@ -1545,6 +1546,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
>  	uint32_t fw_status[3];
>  	uint8_t* fw_sts_ptr;
>  	int res;
> +	struct 	srb_iocb *els;
>  
>  	sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
>  	if (!sp)
> @@ -1561,10 +1563,14 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
>  		break;
>  	case SRB_ELS_DCMD:
>  		type = "Driver ELS logo";
> -		ql_dbg(ql_dbg_user, vha, 0x5047,
> -		    "Completing %s: (%p) type=%d.\n", type, sp, sp->type);
> -		sp->done(sp, 0);
> -		return;
> +		if (iocb_type != ELS_IOCB_TYPE) {
> +			ql_dbg(ql_dbg_user, vha, 0x5047,
> +			    "Completing %s: (%p) type=%d.\n",
> +			    type, sp, sp->type);
> +			sp->done(sp, 0);
> +			return;
> +		}
> +		break;
>  	case SRB_CT_PTHRU_CMD:
>  		/* borrowing sts_entry_24xx.comp_status.
>  		   same location as ct_entry_24xx.comp_status
> @@ -1584,6 +1590,33 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
>  	fw_status[1] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_1);
>  	fw_status[2] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_2);
>  
> +	if (iocb_type == ELS_IOCB_TYPE) {
> +		els = &sp->u.iocb_cmd;
> +		els->u.els_plogi.fw_status[0] = fw_status[0];
> +		els->u.els_plogi.fw_status[1] = fw_status[1];
> +		els->u.els_plogi.fw_status[2] = fw_status[2];
> +		els->u.els_plogi.comp_status = fw_status[0];
> +		if (comp_status == CS_COMPLETE) {
> +			res =  DID_OK << 16;
> +		} else {
> +			if (comp_status == CS_DATA_UNDERRUN) {
> +				res =  DID_OK << 16;
> +				els->u.els_plogi.len =
> +				le16_to_cpu(((struct els_sts_entry_24xx *)
> +					pkt)->total_byte_count);
> +			} else {
> +				els->u.els_plogi.len = 0;
> +				res = DID_ERROR << 16;
> +			}
> +		}
> +		ql_log(ql_log_info, vha, 0x503f,
> +		    "ELS IOCB Done -%s error 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],
> +		    le16_to_cpu(((struct els_sts_entry_24xx *)
> +			pkt)->total_byte_count));
> +		goto els_ct_done;
> +	}
> +
>  	/* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
>  	 * fc payload  to the caller
>  	 */
> @@ -1631,6 +1664,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
>  		bsg_reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len;
>  		bsg_job->reply_len = 0;
>  	}
> +els_ct_done:
>  
>  	sp->done(sp, res);
>  }
> diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
> index 71e56877e1eb..d44f65fe9434 100644
> --- a/drivers/scsi/qla2xxx/qla_mbx.c
> +++ b/drivers/scsi/qla2xxx/qla_mbx.c
> @@ -1786,6 +1786,7 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
>  	if (pd  == NULL) {
>  		ql_log(ql_log_warn, vha, 0x1050,
>  		    "Failed to allocate port database structure.\n");
> +		fcport->query = 0;
>  		return QLA_MEMORY_ALLOC_FAILED;
>  	}
>  
> @@ -1926,6 +1927,7 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
>  
>  gpd_error_out:
>  	dma_pool_free(ha->s_dma_pool, pd, pd_dma);
> +	fcport->query = 0;
>  
>  	if (rval != QLA_SUCCESS) {
>  		ql_dbg(ql_dbg_mbx, vha, 0x1052,
> @@ -3762,6 +3764,38 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
>  			rptid_entry->vp_status,
>  		    rptid_entry->port_id[2], rptid_entry->port_id[1],
>  		    rptid_entry->port_id[0]);
> +		ql_dbg(ql_dbg_async, vha, 0x5075,
> +		   "Format 1: Remote WWPN %8phC.\n",
> +		   rptid_entry->u.f1.port_name);
> +
> +		ql_dbg(ql_dbg_async, vha, 0x5075,
> +		   "Format 1: WWPN %8phC.\n",
> +		   vha->port_name);
> +
> +		/* N2N.  direct connect */
> +		if (IS_QLA27XX(ha) &&
> +		    ((rptid_entry->u.f1.flags>>1) & 0x7) == 2) {
> +			/* if our portname is higher then initiate N2N login */
> +			if (wwn_to_u64(vha->port_name) >
> +			    wwn_to_u64(rptid_entry->u.f1.port_name)) {
> +				// ??? qlt_update_host_map(vha, id);
> +				vha->n2n_id = 0x1;
> +				ql_dbg(ql_dbg_async, vha, 0x5075,
> +				    "Format 1: Setting n2n_update_needed for id %d\n",
> +				    vha->n2n_id);
> +			} else {
> +				ql_dbg(ql_dbg_async, vha, 0x5075,
> +				    "Format 1: Remote login - Waiting for WWPN %8phC.\n",
> +				    rptid_entry->u.f1.port_name);
> +			}
> +
> +			memcpy(vha->n2n_port_name, rptid_entry->u.f1.port_name,
> +			    WWN_SIZE);
> +			set_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags);
> +			set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
> +			set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
> +			return;
> +		}
>  
>  		/* buffer to buffer credit flag */
>  		vha->flags.bbcr_enable = (rptid_entry->u.f1.bbcr & 0xf) != 0;
> @@ -4599,6 +4633,48 @@ qla25xx_set_driver_version(scsi_qla_host_t *vha, char *version)
>  	return rval;
>  }
>  
> +int
> +qla24xx_get_port_login_templ(scsi_qla_host_t *vha, dma_addr_t buf_dma,
> +    void	*buf, uint16_t bufsiz)
> +{
> +	int rval, i;
> +	mbx_cmd_t mc;
> +	mbx_cmd_t *mcp = &mc;
> +	uint32_t	*bp;
> +
> +	if (!IS_FWI2_CAPABLE(vha->hw))
> +		return QLA_FUNCTION_FAILED;
> +
> +	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1159,
> +	    "Entered %s.\n", __func__);
> +
> +	mcp->mb[0] = MBC_GET_RNID_PARAMS;
> +	mcp->mb[1] = RNID_TYPE_PORT_LOGIN << 8;
> +	mcp->mb[2] = MSW(buf_dma);
> +	mcp->mb[3] = LSW(buf_dma);
> +	mcp->mb[6] = MSW(MSD(buf_dma));
> +	mcp->mb[7] = LSW(MSD(buf_dma));
> +	mcp->mb[8] = bufsiz/4;
> +	mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
> +	mcp->in_mb = MBX_1|MBX_0;
> +	mcp->tov = MBX_TOV_SECONDS;
> +	mcp->flags = 0;
> +	rval = qla2x00_mailbox_command(vha, mcp);
> +
> +	if (rval != QLA_SUCCESS) {
> +		ql_dbg(ql_dbg_mbx, vha, 0x115a,
> +		    "Failed=%x mb[0]=%x,%x.\n", rval, mcp->mb[0], mcp->mb[1]);
> +	} else {
> +		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x115b,
> +		    "Done %s.\n", __func__);
> +		bp = (uint32_t *) buf;
> +		for (i = 0; i < (bufsiz-4)/4; i++, bp++)
> +			*bp = cpu_to_be32(*bp);
> +	}
> +
> +	return rval;
> +}
> +
>  static int
>  qla2x00_read_asic_temperature(scsi_qla_host_t *vha, uint16_t *temp)
>  {

Thanks.

Tested-by: Ewan D. Milne <emilne@redhat.com>
diff mbox

Patch

diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index f712c0cd46d6..01a9b8971e88 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -323,6 +323,12 @@  struct els_logo_payload {
 	uint8_t wwpn[WWN_SIZE];
 };
 
+struct els_plogi_payload {
+	uint8_t opcode;
+	uint8_t rsvd[3];
+	uint8_t data[112];
+};
+
 struct ct_arg {
 	void		*iocb;
 	u16		nport_handle;
@@ -358,6 +364,19 @@  struct srb_iocb {
 			dma_addr_t els_logo_pyld_dma;
 		} els_logo;
 		struct {
+#define ELS_DCMD_PLOGI 0x3
+			uint32_t flags;
+			uint32_t els_cmd;
+			struct completion comp;
+			struct els_plogi_payload *els_plogi_pyld;
+			struct els_plogi_payload *els_resp_pyld;
+			dma_addr_t els_plogi_pyld_dma;
+			dma_addr_t els_resp_pyld_dma;
+			uint32_t	fw_status[3];
+			__le16	comp_status;
+			__le16	len;
+		} els_plogi;
+		struct {
 			/*
 			 * Values for flags field below are as
 			 * defined in tsk_mgmt_entry struct
@@ -2349,6 +2368,7 @@  typedef struct fc_port {
 	uint8_t fc4_type;
 	uint8_t	fc4f_nvme;
 	uint8_t scan_state;
+	uint8_t n2n_flag;
 
 	unsigned long last_queue_full;
 	unsigned long last_ramp_up;
@@ -2372,6 +2392,7 @@  typedef struct fc_port {
 	u8 iocb[IOCB_SIZE];
 	u8 current_login_state;
 	u8 last_login_state;
+	struct completion n2n_done;
 } fc_port_t;
 
 #define QLA_FCPORT_SCAN		1
@@ -4228,6 +4249,9 @@  typedef struct scsi_qla_host {
 	wait_queue_head_t fcport_waitQ;
 	wait_queue_head_t vref_waitq;
 	uint8_t min_link_speed_feat;
+	uint8_t n2n_node_name[WWN_SIZE];
+	uint8_t n2n_port_name[WWN_SIZE];
+	uint16_t	n2n_id;
 } scsi_qla_host_t;
 
 struct qla27xx_image_status {
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index bec641aae7b3..d5cef0727e72 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -753,9 +753,7 @@  struct els_entry_24xx {
 	uint8_t reserved_2;
 
 	uint8_t port_id[3];
-	uint8_t reserved_3;
-
-	uint16_t reserved_4;
+	uint8_t s_id[3];
 
 	uint16_t control_flags;		/* Control flags. */
 #define ECF_PAYLOAD_DESCR_MASK	(BIT_15|BIT_14|BIT_13)
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 46c7822c20fc..1e35e961683f 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -45,6 +45,8 @@  extern int qla2x00_fabric_login(scsi_qla_host_t *, fc_port_t *, uint16_t *);
 extern int qla2x00_local_device_login(scsi_qla_host_t *, fc_port_t *);
 
 extern int qla24xx_els_dcmd_iocb(scsi_qla_host_t *, int, port_id_t);
+extern int qla24xx_els_dcmd2_iocb(scsi_qla_host_t *, int, fc_port_t *,
+    port_id_t);
 
 extern void qla2x00_update_fcports(scsi_qla_host_t *);
 
@@ -487,6 +489,8 @@  int qla24xx_gidlist_wait(struct scsi_qla_host *, void *, dma_addr_t,
     uint16_t *);
 int __qla24xx_parse_gpdb(struct scsi_qla_host *, fc_port_t *,
 	struct port_database_24xx *);
+int qla24xx_get_port_login_templ(scsi_qla_host_t *, dma_addr_t,
+    void *, uint16_t);
 
 extern int qla27xx_get_zio_threshold(scsi_qla_host_t *, uint16_t *);
 extern int qla27xx_set_zio_threshold(scsi_qla_host_t *, uint16_t);
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 4c53199db371..b2a391f93775 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -1433,6 +1433,14 @@  qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea)
 		qla24xx_post_gpdb_work(vha, ea->fcport, 0);
 		break;
 	default:
+		if (ea->fcport->n2n_flag) {
+			ql_dbg(ql_dbg_disc, vha, 0x2118,
+				"%s %d %8phC post fc4 prli\n",
+				__func__, __LINE__, ea->fcport->port_name);
+			ea->fcport->fc4f_nvme = 0;
+			ea->fcport->n2n_flag = 0;
+			qla24xx_post_prli_work(vha, ea->fcport);
+		}
 		ql_dbg(ql_dbg_disc, vha, 0x2119,
 		    "%s %d %8phC unhandle event of %x\n",
 		    __func__, __LINE__, ea->fcport->port_name, ea->data[0]);
@@ -4366,7 +4374,109 @@  qla2x00_configure_loop(scsi_qla_host_t *vha)
 	return (rval);
 }
 
+/*
+ * N2N Login
+ *	Updates Fibre Channel Device Database with local loop devices.
+ *
+ * Input:
+ *	ha = adapter block pointer.
+ *
+ * Returns:
+ */
+static int qla24xx_n2n_handle_login(struct scsi_qla_host *vha,
+    fc_port_t *fcport)
+{
+	struct qla_hw_data *ha = vha->hw;
+	int	res = QLA_SUCCESS, rval;
+	int	greater_wwpn = 0;
+	int	logged_in = 0;
+
+	if (ha->current_topology != ISP_CFG_N)
+		return res;
+
+	if (wwn_to_u64(vha->port_name) >
+	    wwn_to_u64(vha->n2n_port_name)) {
+		ql_dbg(ql_dbg_disc, vha, 0x2002,
+		    "HBA WWPN is greater %llx > target %llx\n",
+		    wwn_to_u64(vha->port_name),
+		    wwn_to_u64(vha->n2n_port_name));
+		greater_wwpn = 1;
+		fcport->d_id.b24 = vha->n2n_id;
+	}
+
+	fcport->loop_id = vha->loop_id;
+	fcport->fc4f_nvme = 0;
+	fcport->query = 1;
+
+	ql_dbg(ql_dbg_disc, vha, 0x4001,
+	    "Initiate N2N login handler: HBA port_id=%06x loopid=%d\n",
+	    fcport->d_id.b24, vha->loop_id);
+
+	/* Fill in member data. */
+	if (!greater_wwpn) {
+		rval = qla2x00_get_port_database(vha, fcport, 0);
+		ql_dbg(ql_dbg_disc, vha, 0x1051,
+		    "Remote login-state (%x/%x) port_id=%06x loop_id=%x, rval=%d\n",
+		    fcport->current_login_state, fcport->last_login_state,
+		    fcport->d_id.b24, fcport->loop_id, rval);
+
+		if (((fcport->current_login_state & 0xf) == 0x4) ||
+		    ((fcport->current_login_state & 0xf) == 0x6))
+			logged_in = 1;
+	}
+
+	if (logged_in || greater_wwpn) {
+		if (!vha->nvme_local_port && vha->flags.nvme_enabled)
+			qla_nvme_register_hba(vha);
+
+		/* Set connected N_Port d_id */
+		if (vha->flags.nvme_enabled)
+			fcport->fc4f_nvme = 1;
+
+		fcport->scan_state = QLA_FCPORT_FOUND;
+		fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
+		fcport->disc_state = DSC_GNL;
+		fcport->n2n_flag = 1;
+		fcport->flags = 3;
+		vha->hw->flags.gpsc_supported = 0;
+
+		if (greater_wwpn) {
+			ql_dbg(ql_dbg_disc, vha, 0x20e5,
+			    "%s %d PLOGI ELS %8phC\n",
+			    __func__, __LINE__, fcport->port_name);
+
+			res = qla24xx_els_dcmd2_iocb(vha, ELS_DCMD_PLOGI,
+			    fcport, fcport->d_id);
+		}
+
+		if (res != QLA_SUCCESS) {
+			ql_log(ql_log_info, vha, 0xd04d,
+			    "PLOGI Failed: portid=%06x - retrying\n",
+			    fcport->d_id.b24);
+			res = QLA_SUCCESS;
+		} else {
+			/* State 0x6 means FCP PRLI complete */
+			if ((fcport->current_login_state & 0xf) == 0x6) {
+				ql_dbg(ql_dbg_disc, vha, 0x2118,
+				    "%s %d %8phC post GPDB work\n",
+				    __func__, __LINE__, fcport->port_name);
+				fcport->chip_reset =
+				    vha->hw->base_qpair->chip_reset;
+				qla24xx_post_gpdb_work(vha, fcport, 0);
+			} else {
+				ql_dbg(ql_dbg_disc, vha, 0x2118,
+				    "%s %d %8phC post NVMe PRLI\n",
+				    __func__, __LINE__, fcport->port_name);
+				qla24xx_post_prli_work(vha, fcport);
+			}
+		}
+	} else {
+		/* Wait for next database change */
+		set_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags);
+	}
 
+	return res;
+}
 
 /*
  * qla2x00_configure_local_loop
@@ -4437,6 +4547,14 @@  qla2x00_configure_local_loop(scsi_qla_host_t *vha)
 		}
 	}
 
+	/* Inititae N2N login. */
+	if (test_and_clear_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags)) {
+		rval = qla24xx_n2n_handle_login(vha, new_fcport);
+		if (rval != QLA_SUCCESS)
+			goto cleanup_allocation;
+		return QLA_SUCCESS;
+	}
+
 	/* Add devices to port list. */
 	id_iter = (char *)ha->gid_list;
 	for (index = 0; index < entries; index++) {
@@ -4478,10 +4596,13 @@  qla2x00_configure_local_loop(scsi_qla_host_t *vha)
 			    "Failed to retrieve fcport information "
 			    "-- get_port_database=%x, loop_id=0x%04x.\n",
 			    rval2, new_fcport->loop_id);
-			ql_dbg(ql_dbg_disc, vha, 0x2105,
-			    "Scheduling resync.\n");
-			set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
-			continue;
+			/* Skip retry if N2N */
+			if (ha->current_topology != ISP_CFG_N) {
+				ql_dbg(ql_dbg_disc, vha, 0x2105,
+				    "Scheduling resync.\n");
+				set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+				continue;
+			}
 		}
 
 		spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
@@ -7554,6 +7675,12 @@  qla81xx_nvram_config(scsi_qla_host_t *vha)
 	if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha))
 		icb->firmware_options_3 |= BIT_0;
 
+	if (IS_QLA27XX(ha)) {
+		icb->firmware_options_3 |= BIT_8;
+		ql_dbg(ql_log_info, vha, 0x0075,
+		    "Enabling direct connection.\n");
+	}
+
 	if (rval) {
 		ql_log(ql_log_warn, vha, 0x0076,
 		    "NVRAM configuration failed.\n");
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 2f94159186d7..0502ed7ee14e 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -2518,6 +2518,7 @@  qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
 {
 	scsi_qla_host_t *vha = sp->vha;
 	struct srb_iocb *elsio = &sp->u.iocb_cmd;
+	uint32_t	dsd_len = 24;
 
 	els_iocb->entry_type = ELS_IOCB_TYPE;
 	els_iocb->entry_count = 1;
@@ -2534,24 +2535,198 @@  qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
 	els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
 	els_iocb->port_id[1] = sp->fcport->d_id.b.area;
 	els_iocb->port_id[2] = sp->fcport->d_id.b.domain;
+	els_iocb->s_id[0] = vha->d_id.b.al_pa;
+	els_iocb->s_id[1] = vha->d_id.b.area;
+	els_iocb->s_id[2] = vha->d_id.b.domain;
 	els_iocb->control_flags = 0;
 
-	els_iocb->tx_byte_count = sizeof(struct els_logo_payload);
-	els_iocb->tx_address[0] =
-	    cpu_to_le32(LSD(elsio->u.els_logo.els_logo_pyld_dma));
-	els_iocb->tx_address[1] =
-	    cpu_to_le32(MSD(elsio->u.els_logo.els_logo_pyld_dma));
-	els_iocb->tx_len = cpu_to_le32(sizeof(struct els_logo_payload));
+	if (elsio->u.els_logo.els_cmd == ELS_DCMD_PLOGI) {
+		els_iocb->tx_byte_count = sizeof(struct els_plogi_payload);
+		els_iocb->tx_address[0] =
+			cpu_to_le32(LSD(elsio->u.els_plogi.els_plogi_pyld_dma));
+		els_iocb->tx_address[1] =
+			cpu_to_le32(MSD(elsio->u.els_plogi.els_plogi_pyld_dma));
+		els_iocb->tx_len = dsd_len;
+
+		els_iocb->rx_dsd_count = 1;
+		els_iocb->rx_byte_count = sizeof(struct els_plogi_payload);
+		els_iocb->rx_address[0] =
+			cpu_to_le32(LSD(elsio->u.els_plogi.els_resp_pyld_dma));
+		els_iocb->rx_address[1] =
+			cpu_to_le32(MSD(elsio->u.els_plogi.els_resp_pyld_dma));
+		els_iocb->rx_len = dsd_len;
+		ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3073,
+		    "PLOGI ELS IOCB:\n");
+		ql_dump_buffer(ql_log_info, vha, 0x0109,
+		    (uint8_t *)els_iocb, 0x70);
+	} else {
+		els_iocb->tx_byte_count = sizeof(struct els_logo_payload);
+		els_iocb->tx_address[0] =
+		    cpu_to_le32(LSD(elsio->u.els_logo.els_logo_pyld_dma));
+		els_iocb->tx_address[1] =
+		    cpu_to_le32(MSD(elsio->u.els_logo.els_logo_pyld_dma));
+		els_iocb->tx_len = cpu_to_le32(sizeof(struct els_logo_payload));
 
-	els_iocb->rx_byte_count = 0;
-	els_iocb->rx_address[0] = 0;
-	els_iocb->rx_address[1] = 0;
-	els_iocb->rx_len = 0;
+		els_iocb->rx_byte_count = 0;
+		els_iocb->rx_address[0] = 0;
+		els_iocb->rx_address[1] = 0;
+		els_iocb->rx_len = 0;
+	}
 
 	sp->vha->qla_stats.control_requests++;
 }
 
 static void
+qla2x00_els_dcmd2_sp_free(void *data)
+{
+	srb_t *sp = data;
+	struct srb_iocb *elsio = &sp->u.iocb_cmd;
+
+	if (elsio->u.els_plogi.els_plogi_pyld)
+		dma_free_coherent(&sp->vha->hw->pdev->dev, DMA_POOL_SIZE,
+		    elsio->u.els_plogi.els_plogi_pyld,
+		    elsio->u.els_plogi.els_plogi_pyld_dma);
+
+	if (elsio->u.els_plogi.els_resp_pyld)
+		dma_free_coherent(&sp->vha->hw->pdev->dev, DMA_POOL_SIZE,
+		    elsio->u.els_plogi.els_resp_pyld,
+		    elsio->u.els_plogi.els_resp_pyld_dma);
+
+	del_timer(&elsio->timer);
+	qla2x00_rel_sp(sp);
+}
+
+static void
+qla2x00_els_dcmd2_iocb_timeout(void *data)
+{
+	srb_t *sp = data;
+	fc_port_t *fcport = sp->fcport;
+	struct scsi_qla_host *vha = sp->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct srb_iocb *lio = &sp->u.iocb_cmd;
+	unsigned long flags = 0;
+	int res;
+
+	ql_dbg(ql_dbg_io + ql_dbg_disc, vha, 0x3069,
+	    "%s hdl=%x ELS Timeout, %8phC portid=%06x\n",
+	    sp->name, sp->handle, fcport->port_name, fcport->d_id.b24);
+
+	/* Abort the exchange */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	res = ha->isp_ops->abort_command(sp);
+	ql_dbg(ql_dbg_io, vha, 0x3070,
+	    "mbx abort_command %s\n",
+	    (res == QLA_SUCCESS) ? "successful" : "failed");
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	complete(&lio->u.els_plogi.comp);
+}
+
+static void
+qla2x00_els_dcmd2_sp_done(void *ptr, int res)
+{
+	srb_t *sp = ptr;
+	fc_port_t *fcport = sp->fcport;
+	struct srb_iocb *lio = &sp->u.iocb_cmd;
+	struct scsi_qla_host *vha = sp->vha;
+
+	ql_dbg(ql_dbg_io + ql_dbg_disc, vha, 0x3072,
+	    "%s ELS hdl=%x, portid=%06x done %8pC\n",
+	    sp->name, sp->handle, fcport->d_id.b24, fcport->port_name);
+
+	complete(&lio->u.els_plogi.comp);
+}
+
+int
+qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode,
+    fc_port_t *fcport, port_id_t remote_did)
+{
+	srb_t *sp;
+	struct srb_iocb *elsio = NULL;
+	struct qla_hw_data *ha = vha->hw;
+	int rval = QLA_SUCCESS;
+	void	*ptr, *resp_ptr;
+	dma_addr_t ptr_dma;
+
+	/* Alloc SRB structure */
+	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+	if (!sp) {
+		ql_log(ql_log_info, vha, 0x70e6,
+		 "SRB allocation failed\n");
+		return -ENOMEM;
+	}
+
+	elsio = &sp->u.iocb_cmd;
+	fcport->d_id.b.domain = remote_did.b.domain;
+	fcport->d_id.b.area = remote_did.b.area;
+	fcport->d_id.b.al_pa = remote_did.b.al_pa;
+
+	ql_dbg(ql_dbg_io, vha, 0x3073,
+	    "Enter: PLOGI portid=%06x\n", fcport->d_id.b24);
+
+	sp->type = SRB_ELS_DCMD;
+	sp->name = "ELS_DCMD";
+	sp->fcport = fcport;
+	qla2x00_init_timer(sp, ELS_DCMD_TIMEOUT);
+	elsio->timeout = qla2x00_els_dcmd2_iocb_timeout;
+	sp->done = qla2x00_els_dcmd2_sp_done;
+	sp->free = qla2x00_els_dcmd2_sp_free;
+
+	ptr = elsio->u.els_plogi.els_plogi_pyld =
+	    dma_alloc_coherent(&ha->pdev->dev, DMA_POOL_SIZE,
+		&elsio->u.els_plogi.els_plogi_pyld_dma, GFP_KERNEL);
+	ptr_dma = elsio->u.els_plogi.els_plogi_pyld_dma;
+
+	if (!elsio->u.els_plogi.els_plogi_pyld) {
+		rval = QLA_FUNCTION_FAILED;
+		goto out;
+	}
+
+	resp_ptr = elsio->u.els_plogi.els_resp_pyld =
+	    dma_alloc_coherent(&ha->pdev->dev, DMA_POOL_SIZE,
+		&elsio->u.els_plogi.els_resp_pyld_dma, GFP_KERNEL);
+
+	if (!elsio->u.els_plogi.els_resp_pyld) {
+		rval = QLA_FUNCTION_FAILED;
+		goto out;
+	}
+
+	ql_dbg(ql_dbg_io, vha, 0x3073, "PLOGI %p %p\n", ptr, resp_ptr);
+
+	memset(ptr, 0, sizeof(struct els_plogi_payload));
+	memset(resp_ptr, 0, sizeof(struct els_plogi_payload));
+	elsio->u.els_plogi.els_cmd = els_opcode;
+	elsio->u.els_plogi.els_plogi_pyld->opcode = els_opcode;
+	qla24xx_get_port_login_templ(vha, ptr_dma + 4,
+		&elsio->u.els_plogi.els_plogi_pyld->data[0],
+		sizeof(struct els_plogi_payload));
+
+	ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3073, "PLOGI buffer:\n");
+	ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x0109,
+	    (uint8_t *)elsio->u.els_plogi.els_plogi_pyld, 0x70);
+
+	init_completion(&elsio->u.els_plogi.comp);
+	rval = qla2x00_start_sp(sp);
+	if (rval != QLA_SUCCESS) {
+		rval = QLA_FUNCTION_FAILED;
+		goto out;
+	}
+
+	ql_dbg(ql_dbg_io, vha, 0x3074,
+	    "%s PLOGI sent, hdl=%x, loopid=%x, portid=%06x\n",
+	    sp->name, sp->handle, fcport->loop_id, fcport->d_id.b24);
+
+	wait_for_completion(&elsio->u.els_plogi.comp);
+
+	if (elsio->u.els_plogi.comp_status != CS_COMPLETE)
+		rval = QLA_FUNCTION_FAILED;
+
+out:
+	sp->free(sp);
+	return rval;
+}
+
+static void
 qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
 {
 	struct bsg_job *bsg_job = sp->u.bsg_job;
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index ab97fb06c239..d06a1a809188 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -1041,6 +1041,7 @@  qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
 		 */
 		atomic_set(&vha->loop_down_timer, 0);
 		if (atomic_read(&vha->loop_state) != LOOP_DOWN &&
+			!ha->flags.n2n_ae  &&
 		    atomic_read(&vha->loop_state) != LOOP_DEAD) {
 			ql_dbg(ql_dbg_async, vha, 0x5011,
 			    "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n",
@@ -1545,6 +1546,7 @@  qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
 	uint32_t fw_status[3];
 	uint8_t* fw_sts_ptr;
 	int res;
+	struct 	srb_iocb *els;
 
 	sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 	if (!sp)
@@ -1561,10 +1563,14 @@  qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
 		break;
 	case SRB_ELS_DCMD:
 		type = "Driver ELS logo";
-		ql_dbg(ql_dbg_user, vha, 0x5047,
-		    "Completing %s: (%p) type=%d.\n", type, sp, sp->type);
-		sp->done(sp, 0);
-		return;
+		if (iocb_type != ELS_IOCB_TYPE) {
+			ql_dbg(ql_dbg_user, vha, 0x5047,
+			    "Completing %s: (%p) type=%d.\n",
+			    type, sp, sp->type);
+			sp->done(sp, 0);
+			return;
+		}
+		break;
 	case SRB_CT_PTHRU_CMD:
 		/* borrowing sts_entry_24xx.comp_status.
 		   same location as ct_entry_24xx.comp_status
@@ -1584,6 +1590,33 @@  qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
 	fw_status[1] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_1);
 	fw_status[2] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_2);
 
+	if (iocb_type == ELS_IOCB_TYPE) {
+		els = &sp->u.iocb_cmd;
+		els->u.els_plogi.fw_status[0] = fw_status[0];
+		els->u.els_plogi.fw_status[1] = fw_status[1];
+		els->u.els_plogi.fw_status[2] = fw_status[2];
+		els->u.els_plogi.comp_status = fw_status[0];
+		if (comp_status == CS_COMPLETE) {
+			res =  DID_OK << 16;
+		} else {
+			if (comp_status == CS_DATA_UNDERRUN) {
+				res =  DID_OK << 16;
+				els->u.els_plogi.len =
+				le16_to_cpu(((struct els_sts_entry_24xx *)
+					pkt)->total_byte_count);
+			} else {
+				els->u.els_plogi.len = 0;
+				res = DID_ERROR << 16;
+			}
+		}
+		ql_log(ql_log_info, vha, 0x503f,
+		    "ELS IOCB Done -%s error 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],
+		    le16_to_cpu(((struct els_sts_entry_24xx *)
+			pkt)->total_byte_count));
+		goto els_ct_done;
+	}
+
 	/* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
 	 * fc payload  to the caller
 	 */
@@ -1631,6 +1664,7 @@  qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
 		bsg_reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len;
 		bsg_job->reply_len = 0;
 	}
+els_ct_done:
 
 	sp->done(sp, res);
 }
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 71e56877e1eb..d44f65fe9434 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1786,6 +1786,7 @@  qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
 	if (pd  == NULL) {
 		ql_log(ql_log_warn, vha, 0x1050,
 		    "Failed to allocate port database structure.\n");
+		fcport->query = 0;
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
@@ -1926,6 +1927,7 @@  qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
 
 gpd_error_out:
 	dma_pool_free(ha->s_dma_pool, pd, pd_dma);
+	fcport->query = 0;
 
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x1052,
@@ -3762,6 +3764,38 @@  qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 			rptid_entry->vp_status,
 		    rptid_entry->port_id[2], rptid_entry->port_id[1],
 		    rptid_entry->port_id[0]);
+		ql_dbg(ql_dbg_async, vha, 0x5075,
+		   "Format 1: Remote WWPN %8phC.\n",
+		   rptid_entry->u.f1.port_name);
+
+		ql_dbg(ql_dbg_async, vha, 0x5075,
+		   "Format 1: WWPN %8phC.\n",
+		   vha->port_name);
+
+		/* N2N.  direct connect */
+		if (IS_QLA27XX(ha) &&
+		    ((rptid_entry->u.f1.flags>>1) & 0x7) == 2) {
+			/* if our portname is higher then initiate N2N login */
+			if (wwn_to_u64(vha->port_name) >
+			    wwn_to_u64(rptid_entry->u.f1.port_name)) {
+				// ??? qlt_update_host_map(vha, id);
+				vha->n2n_id = 0x1;
+				ql_dbg(ql_dbg_async, vha, 0x5075,
+				    "Format 1: Setting n2n_update_needed for id %d\n",
+				    vha->n2n_id);
+			} else {
+				ql_dbg(ql_dbg_async, vha, 0x5075,
+				    "Format 1: Remote login - Waiting for WWPN %8phC.\n",
+				    rptid_entry->u.f1.port_name);
+			}
+
+			memcpy(vha->n2n_port_name, rptid_entry->u.f1.port_name,
+			    WWN_SIZE);
+			set_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags);
+			set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
+			set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
+			return;
+		}
 
 		/* buffer to buffer credit flag */
 		vha->flags.bbcr_enable = (rptid_entry->u.f1.bbcr & 0xf) != 0;
@@ -4599,6 +4633,48 @@  qla25xx_set_driver_version(scsi_qla_host_t *vha, char *version)
 	return rval;
 }
 
+int
+qla24xx_get_port_login_templ(scsi_qla_host_t *vha, dma_addr_t buf_dma,
+    void	*buf, uint16_t bufsiz)
+{
+	int rval, i;
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	uint32_t	*bp;
+
+	if (!IS_FWI2_CAPABLE(vha->hw))
+		return QLA_FUNCTION_FAILED;
+
+	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1159,
+	    "Entered %s.\n", __func__);
+
+	mcp->mb[0] = MBC_GET_RNID_PARAMS;
+	mcp->mb[1] = RNID_TYPE_PORT_LOGIN << 8;
+	mcp->mb[2] = MSW(buf_dma);
+	mcp->mb[3] = LSW(buf_dma);
+	mcp->mb[6] = MSW(MSD(buf_dma));
+	mcp->mb[7] = LSW(MSD(buf_dma));
+	mcp->mb[8] = bufsiz/4;
+	mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->in_mb = MBX_1|MBX_0;
+	mcp->tov = MBX_TOV_SECONDS;
+	mcp->flags = 0;
+	rval = qla2x00_mailbox_command(vha, mcp);
+
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0x115a,
+		    "Failed=%x mb[0]=%x,%x.\n", rval, mcp->mb[0], mcp->mb[1]);
+	} else {
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x115b,
+		    "Done %s.\n", __func__);
+		bp = (uint32_t *) buf;
+		for (i = 0; i < (bufsiz-4)/4; i++, bp++)
+			*bp = cpu_to_be32(*bp);
+	}
+
+	return rval;
+}
+
 static int
 qla2x00_read_asic_temperature(scsi_qla_host_t *vha, uint16_t *temp)
 {