Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit edd05de1 authored by Duane Grigsby's avatar Duane Grigsby Committed by Martin K. Petersen
Browse files

scsi: qla2xxx: Changes to support N2N logins



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.

[mkp: many whitespace errors]

Signed-off-by: default avatarDuane Grigsby <duane.grigsby@cavium.com>
Signed-off-by: default avatarMichael Hernandez <michael.hernandez@cavium.com>
Signed-off-by: default avatarHimanshu Madhani <himanshu.madhani@cavium.com>
Reviewed-by: default avatarJohannes Thumshirn <jthumshirn@suse.de>
Tested-by: default avatarEwan D. Milne <emilne@redhat.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent c0c462c8
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -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 {
+1 −3
Original line number Diff line number Diff line
@@ -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)
+4 −0
Original line number Diff line number Diff line
@@ -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);
+131 −4
Original line number Diff line number Diff line
@@ -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,11 +4596,14 @@ 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);
			/* 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);
		/* Check for matching device in port list. */
@@ -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");
+185 −10
Original line number Diff line number Diff line
@@ -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,8 +2535,31 @@ 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;

	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));
@@ -2547,10 +2571,161 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
		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)
{
Loading