diff mbox

[06/14] lpfc: Add support for RDP ELS command.

Message ID 5535058d.LpgX8LwxtB5bdZhy%james.smart@emulex.com (mailing list archive)
State New, archived
Headers show

Commit Message

James Smart April 20, 2015, 1:56 p.m. UTC
Add support for RDP ELS command.

Signed-off-by: Dick Kennedy <dick.kennedy@emulex.com>
Signed-off-by: James Smart <james.smart@emulex.com>
---
 drivers/scsi/lpfc/lpfc.h      |   1 +
 drivers/scsi/lpfc/lpfc_crtn.h |   2 +
 drivers/scsi/lpfc/lpfc_els.c  | 415 ++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/lpfc/lpfc_hw.h   | 169 +++++++++++++++++
 drivers/scsi/lpfc/lpfc_hw4.h  | 201 ++++++++++++++++++++
 drivers/scsi/lpfc/lpfc_mbox.c | 152 ++++++++++++++++
 drivers/scsi/lpfc/lpfc_sli4.h |  10 +
 7 files changed, 950 insertions(+)

Comments

Hannes Reinecke April 21, 2015, 10:05 a.m. UTC | #1
On 04/20/2015 03:56 PM, James Smart wrote:
> 
> Add support for RDP ELS command.
> 
> Signed-off-by: Dick Kennedy <dick.kennedy@emulex.com>
> Signed-off-by: James Smart <james.smart@emulex.com>
> ---
Reviewed-by: Hannes Reinecke <hare@suse.de>

Cheers,

Hannes
Sebastian Herbszt April 21, 2015, 12:38 p.m. UTC | #2
James Smart wrote:
> 
> Add support for RDP ELS command.
> 
> Signed-off-by: Dick Kennedy <dick.kennedy@emulex.com>
> Signed-off-by: James Smart <james.smart@emulex.com>
> ---
>  drivers/scsi/lpfc/lpfc.h      |   1 +
>  drivers/scsi/lpfc/lpfc_crtn.h |   2 +
>  drivers/scsi/lpfc/lpfc_els.c  | 415 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/scsi/lpfc/lpfc_hw.h   | 169 +++++++++++++++++
>  drivers/scsi/lpfc/lpfc_hw4.h  | 201 ++++++++++++++++++++
>  drivers/scsi/lpfc/lpfc_mbox.c | 152 ++++++++++++++++
>  drivers/scsi/lpfc/lpfc_sli4.h |  10 +
>  7 files changed, 950 insertions(+)
> 
> diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
> index 3246d09..a5a56fa 100644
> --- a/drivers/scsi/lpfc/lpfc.h
> +++ b/drivers/scsi/lpfc/lpfc.h
> @@ -231,6 +231,7 @@ struct lpfc_stats {
>  	uint32_t elsRcvRTV;
>  	uint32_t elsRcvECHO;
>  	uint32_t elsRcvLCB;
> +	uint32_t elsRcvRDP;
>  	uint32_t elsXmitFLOGI;
>  	uint32_t elsXmitFDISC;
>  	uint32_t elsXmitPLOGI;
> diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
> index 587e3e9..b0e6fe4 100644
> --- a/drivers/scsi/lpfc/lpfc_crtn.h
> +++ b/drivers/scsi/lpfc/lpfc_crtn.h
> @@ -498,3 +498,5 @@ bool lpfc_disable_oas_lun(struct lpfc_hba *, struct lpfc_name *,
>  bool lpfc_find_next_oas_lun(struct lpfc_hba *, struct lpfc_name *,
>  			    struct lpfc_name *, uint64_t *, struct lpfc_name *,
>  			    struct lpfc_name *, uint64_t *, uint32_t *);
> +int lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox);
> +void lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb);
> diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
> index 9099a06..455a5dd 100644
> --- a/drivers/scsi/lpfc/lpfc_els.c
> +++ b/drivers/scsi/lpfc/lpfc_els.c
> @@ -4617,6 +4617,417 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport)
>  	return sentplogi;
>  }
>  
> +void
> +lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc,
> +		uint32_t word0)
> +{
> +
> +	desc->tag = cpu_to_be32(RDP_LINK_SERVICE_DESC_TAG);
> +	desc->payload.els_req = word0;
> +	desc->length = cpu_to_be32(sizeof(desc->payload));
> +}
> +
> +void
> +lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc,
> +		uint8_t *page_a0, uint8_t *page_a2)
> +{
> +	uint16_t wavelength;
> +	uint16_t temperature;
> +	uint16_t rx_power;
> +	uint16_t tx_bias;
> +	uint16_t tx_power;
> +	uint16_t vcc;
> +	uint16_t flag = 0;
> +	struct sff_trasnceiver_codes_byte4 *trasn_code_byte4;
> +	struct sff_trasnceiver_codes_byte5 *trasn_code_byte5;
> +
> +	desc->tag = cpu_to_be32(RDP_SFP_DESC_TAG);
> +
> +	trasn_code_byte4 = (struct sff_trasnceiver_codes_byte4 *)
> +			&page_a0[SSF_TRANSCEIVER_CODE_B4];
> +	trasn_code_byte5 = (struct sff_trasnceiver_codes_byte5 *)
> +			&page_a0[SSF_TRANSCEIVER_CODE_B5];
> +
> +	if ((trasn_code_byte4->fc_sw_laser) ||
> +	    (trasn_code_byte5->fc_sw_laser_sl) ||
> +	    (trasn_code_byte5->fc_sw_laser_sn)) {  /* check if its short WL */
> +		flag |= (SFP_FLAG_PT_SWLASER << SFP_FLAG_PT_SHIFT);
> +	} else if (trasn_code_byte4->fc_lw_laser) {
> +		wavelength = (page_a0[SSF_WAVELENGTH_B1] << 8) |
> +			page_a0[SSF_WAVELENGTH_B0];
> +		if (wavelength == SFP_WAVELENGTH_LC1310)
> +			flag |= SFP_FLAG_PT_LWLASER_LC1310 << SFP_FLAG_PT_SHIFT;
> +		if (wavelength == SFP_WAVELENGTH_LL1550)
> +			flag |= SFP_FLAG_PT_LWLASER_LL1550 << SFP_FLAG_PT_SHIFT;
> +	}
> +	/* check if its SFP+ */
> +	flag |= ((page_a0[SSF_IDENTIFIER] == SFF_PG0_IDENT_SFP) ?
> +			SFP_FLAG_CT_SFP_PLUS : SFP_FLAG_CT_UNKNOWN)
> +					<< SFP_FLAG_CT_SHIFT;
> +
> +	/* check if its OPTICAL */
> +	flag |= ((page_a0[SSF_CONNECTOR] == SFF_PG0_CONNECTOR_LC) ?
> +			SFP_FLAG_IS_OPTICAL_PORT : 0)
> +					<< SFP_FLAG_IS_OPTICAL_SHIFT;
> +
> +	temperature = (page_a2[SFF_TEMPERATURE_B1] << 8 |
> +		page_a2[SFF_TEMPERATURE_B0]);
> +	vcc = (page_a2[SFF_VCC_B1] << 8 |
> +		page_a2[SFF_VCC_B0]);
> +	tx_power = (page_a2[SFF_TXPOWER_B1] << 8 |
> +		page_a2[SFF_TXPOWER_B0]);
> +	tx_bias = (page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 |
> +		page_a2[SFF_TX_BIAS_CURRENT_B0]);
> +	rx_power = (page_a2[SFF_RXPOWER_B1] << 8 |
> +		page_a2[SFF_RXPOWER_B0]);
> +	desc->sfp_info.temperature = cpu_to_be16(temperature);
> +	desc->sfp_info.rx_power = cpu_to_be16(rx_power);
> +	desc->sfp_info.tx_bias = cpu_to_be16(tx_bias);
> +	desc->sfp_info.tx_power = cpu_to_be16(tx_power);
> +	desc->sfp_info.vcc = cpu_to_be16(vcc);
> +
> +	desc->sfp_info.flags = cpu_to_be16(flag);
> +	desc->length = cpu_to_be32(sizeof(desc->sfp_info));
> +}
> +
> +void
> +lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc,
> +		READ_LNK_VAR *stat)
> +{
> +	uint32_t type;
> +
> +	desc->tag = cpu_to_be32(RDP_LINK_ERROR_STATUS_DESC_TAG);
> +
> +	type = VN_PT_PHY_PF_PORT << VN_PT_PHY_SHIFT;
> +
> +	desc->info.port_type = cpu_to_be32(type);
> +
> +	desc->info.link_status.link_failure_cnt =
> +		cpu_to_be32(stat->linkFailureCnt);
> +	desc->info.link_status.loss_of_synch_cnt =
> +		cpu_to_be32(stat->lossSyncCnt);
> +	desc->info.link_status.loss_of_signal_cnt =
> +		cpu_to_be32(stat->lossSignalCnt);
> +	desc->info.link_status.primitive_seq_proto_err =
> +		cpu_to_be32(stat->primSeqErrCnt);
> +	desc->info.link_status.invalid_trans_word =
> +		cpu_to_be32(stat->invalidXmitWord);
> +	desc->info.link_status.invalid_crc_cnt = cpu_to_be32(stat->crcCnt);
> +
> +	desc->length = cpu_to_be32(sizeof(desc->info));
> +}
> +
> +void
> +lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
> +{
> +	uint16_t rdp_cap = 0;
> +	uint16_t rdp_speed;
> +
> +	desc->tag = cpu_to_be32(RDP_PORT_SPEED_DESC_TAG);
> +
> +	switch (phba->sli4_hba.link_state.speed) {
> +	case LPFC_FC_LA_SPEED_1G:
> +		rdp_speed = RDP_PS_1GB;
> +		break;
> +	case LPFC_FC_LA_SPEED_2G:
> +		rdp_speed = RDP_PS_2GB;
> +		break;
> +	case LPFC_FC_LA_SPEED_4G:
> +		rdp_speed  = RDP_PS_4GB;
> +		break;

Additional space here

> +	case LPFC_FC_LA_SPEED_8G:
> +		rdp_speed = RDP_PS_8GB;
> +		break;
> +	case LPFC_FC_LA_SPEED_10G:
> +		rdp_speed = RDP_PS_10GB;
> +		break;
> +	case LPFC_FC_LA_SPEED_16G:
> +		rdp_speed = RDP_PS_16GB;
> +		break;
> +	case LPFC_FC_LA_SPEED_32G:
> +		rdp_speed = RDP_PS_32GB;
> +		break;
> +	default:
> +		rdp_speed = RDP_PS_UNKNOWN;
> +		break;
> +	}
> +
> +	desc->info.port_speed.speed = cpu_to_be16(rdp_speed);
> +
> +	if (phba->lmt & LMT_16Gb)
> +		rdp_cap |= RDP_PS_16GB;
> +	if (phba->lmt & LMT_10Gb)
> +		rdp_cap |= RDP_PS_10GB;
> +	if (phba->lmt & LMT_8Gb)
> +		rdp_cap |= RDP_PS_8GB;
> +	if (phba->lmt & LMT_4Gb)
> +		rdp_cap |= RDP_PS_4GB;
> +	if (phba->lmt & LMT_2Gb)
> +		rdp_cap |= RDP_PS_2GB;
> +	if (phba->lmt & LMT_1Gb)
> +		rdp_cap |= RDP_PS_1GB;
> +
> +	if (rdp_cap == 0)
> +		rdp_cap = RDP_CAP_UNKNOWN;
> +
> +	desc->info.port_speed.capabilities = cpu_to_be16(rdp_cap);
> +	desc->length = cpu_to_be32(sizeof(desc->info));
> +}
> +
> +void
> +lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc,
> +		struct lpfc_hba  *phba)

and here

> +{
> +
> +	desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
> +
> +	memcpy(desc->port_names.wwnn, phba->wwnn,
> +			sizeof(desc->port_names.wwnn));
> +
> +	memcpy(desc->port_names.wwpn, &phba->wwpn,
> +			sizeof(desc->port_names.wwpn));
> +
> +	desc->length = cpu_to_be32(sizeof(desc->port_names));
> +}
> +
> +void
> +lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc,
> +		struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
> +{
> +
> +	desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
> +	if (vport->fc_flag & FC_FABRIC) {
> +		memcpy(desc->port_names.wwnn, &vport->fabric_nodename,
> +				sizeof(desc->port_names.wwnn));
> +
> +		memcpy(desc->port_names.wwpn, &vport->fabric_portname,
> +				sizeof(desc->port_names.wwpn));
> +	} else {  /* Point to Point */
> +		memcpy(desc->port_names.wwnn, &ndlp->nlp_nodename,
> +				sizeof(desc->port_names.wwnn));
> +
> +		memcpy(desc->port_names.wwnn, &ndlp->nlp_portname,
> +				sizeof(desc->port_names.wwpn));
> +	}
> +
> +	desc->length = cpu_to_be32(sizeof(desc->port_names));
> +}
> +
> +void
> +lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context,
> +		int status)
> +{
> +	struct lpfc_nodelist *ndlp = rdp_context->ndlp;
> +	struct lpfc_vport *vport = ndlp->vport;
> +	struct lpfc_iocbq *elsiocb;
> +	IOCB_t *icmd;
> +	uint8_t *pcmd;
> +	struct ls_rjt *stat;
> +	struct fc_rdp_res_frame *rdp_res;
> +	uint32_t cmdsize;
> +	int rc;
> +
> +	if (status != SUCCESS)
> +		goto error;
> +	cmdsize = sizeof(struct fc_rdp_res_frame);
> +
> +	elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize,
> +			lpfc_max_els_tries, rdp_context->ndlp,
> +			rdp_context->ndlp->nlp_DID, ELS_CMD_ACC);
> +	lpfc_nlp_put(ndlp);
> +	if (!elsiocb)
> +		goto free_rdp_context;
> +
> +	icmd = &elsiocb->iocb;
> +	icmd->ulpContext = rdp_context->rx_id;
> +	icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
> +
> +	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
> +			"2171 Xmit RDP response tag x%x xri x%x, "
> +			"did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x",
> +			elsiocb->iotag, elsiocb->iocb.ulpContext,
> +			ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
> +			ndlp->nlp_rpi);
> +	rdp_res = (struct fc_rdp_res_frame *)
> +		(((struct lpfc_dmabuf *) elsiocb->context2)->virt);
> +	pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
> +	memset(pcmd, 0, sizeof(struct fc_rdp_res_frame));
> +	*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
> +
> +	/* For RDP payload */
> +	lpfc_rdp_res_link_service(&rdp_res->link_service_desc, ELS_CMD_RDP);
> +
> +	lpfc_rdp_res_sfp_desc(&rdp_res->sfp_desc,
> +			rdp_context->page_a0, rdp_context->page_a2);
> +	lpfc_rdp_res_speed(&rdp_res->portspeed_desc, phba);
> +	lpfc_rdp_res_link_error(&rdp_res->link_error_desc,
> +			&rdp_context->link_stat);
> +	lpfc_rdp_res_diag_port_names(&rdp_res->diag_port_names_desc, phba);
> +	lpfc_rdp_res_attach_port_names(&rdp_res->attached_port_names_desc,
> +			vport, ndlp);
> +	rdp_res->length = cpu_to_be32(RDP_DESC_PAYLOAD_SIZE);
> +
> +	elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
> +
> +	phba->fc_stat.elsXmitACC++;
> +	rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
> +	if (rc == IOCB_ERROR)
> +		lpfc_els_free_iocb(phba, elsiocb);
> +
> +	kfree(rdp_context);
> +
> +	return;
> +error:
> +	cmdsize = 2 * sizeof(uint32_t);
> +	elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, lpfc_max_els_tries,
> +			ndlp, ndlp->nlp_DID, ELS_CMD_LS_RJT);
> +	lpfc_nlp_put(ndlp);
> +	if (!elsiocb)
> +		goto free_rdp_context;
> +
> +	icmd = &elsiocb->iocb;
> +	icmd->ulpContext = rdp_context->rx_id;
> +	icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
> +	pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
> +
> +	*((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
> +	stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t));
> +	stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
> +
> +	phba->fc_stat.elsXmitLSRJT++;
> +	elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
> +	rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
> +
> +	if (rc == IOCB_ERROR)
> +		lpfc_els_free_iocb(phba, elsiocb);
> +free_rdp_context:
> +	kfree(rdp_context);
> +}
> +
> +int
> +lpfc_get_rdp_info(struct lpfc_hba  *phba, struct lpfc_rdp_context *rdp_context)
> +{

and here

> +	LPFC_MBOXQ_t *mbox = NULL;
> +	int rc;
> +
> +	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
> +	if (!mbox) {
> +		lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS,
> +				"7105 failed to allocate mailbox memory");
> +		return 1;
> +	}
> +
> +	if (lpfc_sli4_dump_page_a0(phba, mbox))
> +		goto prep_mbox_fail;
> +	mbox->vport = rdp_context->ndlp->vport;
> +	mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a0;
> +	mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
> +	rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
> +	if (rc == MBX_NOT_FINISHED)
> +		goto issue_mbox_fail;
> +
> +	return 0;
> +
> +prep_mbox_fail:
> +issue_mbox_fail:
> +	mempool_free(mbox, phba->mbox_mem_pool);
> +	return 1;
> +}
> +
> +/*
> + * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS.
> + * @vport: pointer to a host virtual N_Port data structure.
> + * @cmdiocb: pointer to lpfc command iocb data structure.
> + * @ndlp: pointer to a node-list data structure.
> + *
> + * This routine processes an unsolicited RDP(Read Diagnostic Parameters)
> + * IOCB. First, the payload of the unsolicited RDP is checked.
> + * Then it will (1) send MBX_DUMP_MEMORY, Embedded DMP_LMSD sub command TYPE-3
> + * for Page A0, (2) send MBX_DUMP_MEMORY, DMP_LMSD for Page A2,
> + * (3) send MBX_READ_LNK_STAT to get link stat, (4) Call lpfc_els_rdp_cmpl
> + * gather all data and send RDP response.
> + *
> + * Return code
> + *   0 - Sent the acc response
> + *   1 - Sent the reject response.
> + */
> +static int
> +lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
> +		struct lpfc_nodelist *ndlp)
> +{
> +	struct lpfc_hba  *phba = vport->phba;

here too

> +	struct lpfc_dmabuf *pcmd;
> +	uint8_t rjt_err;
> +	struct fc_rdp_req_frame *rdp_req;
> +	struct lpfc_rdp_context *rdp_context;
> +	IOCB_t *cmd = NULL;
> +	struct ls_rjt stat;
> +
> +	if (phba->sli_rev < LPFC_SLI_REV4 ||
> +			(bf_get(lpfc_sli_intf_if_type,
> +				&phba->sli4_hba.sli_intf) !=
> +						LPFC_SLI_INTF_IF_TYPE_2)) {
> +		rjt_err = LSRJT_CMD_UNSUPPORTED;
> +		goto rjt;
> +	}
> +
> +	if (phba->sli_rev < LPFC_SLI_REV4 || (phba->hba_flag & HBA_FCOE_MODE)) {
> +		rjt_err = LSRJT_CMD_UNSUPPORTED;
> +		goto rjt;
> +	}
> +

Code at rjt label also sets rjt_err to LSRJT_CMD_UNSUPPORTED.

Should the command not get rejected with LSRJT_UNABLE_TPC and
LSEXP_REQ_UNSUPPORTED?

> +	pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
> +	rdp_req = (struct fc_rdp_req_frame *) pcmd->virt;
> +
> +
> +	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
> +			 "2422 ELS RDP Request "
> +			 "dec len %d tag x%x port_id %d len %d\n",
> +			 be32_to_cpu(rdp_req->rdp_des_length),
> +			 be32_to_cpu(rdp_req->nport_id_desc.tag),
> +			 be32_to_cpu(rdp_req->nport_id_desc.nport_id),
> +			 be32_to_cpu(rdp_req->nport_id_desc.length));
> +
> +	if (sizeof(struct fc_rdp_nport_desc) !=
> +			be32_to_cpu(rdp_req->rdp_des_length))
> +		goto rjt;
> +	if (RDP_N_PORT_DESC_TAG != be32_to_cpu(rdp_req->nport_id_desc.tag))
> +		goto rjt;
> +	if (RDP_NPORT_ID_SIZE !=
> +			be32_to_cpu(rdp_req->nport_id_desc.length))
> +		goto rjt;
> +	rdp_context = kmalloc(sizeof(struct lpfc_rdp_context), GFP_KERNEL);
> +	if (!rdp_context) {
> +		rjt_err = LSRJT_UNABLE_TPC;
> +		goto error;
> +	}
> +
> +	memset(rdp_context, 0, sizeof(struct lpfc_rdp_context));
> +	cmd = &cmdiocb->iocb;
> +	rdp_context->ndlp = lpfc_nlp_get(ndlp);
> +	rdp_context->ox_id = cmd->unsli3.rcvsli3.ox_id;
> +	rdp_context->rx_id = cmd->ulpContext;
> +	rdp_context->cmpl = lpfc_els_rdp_cmpl;
> +	if (lpfc_get_rdp_info(phba, rdp_context)) {
> +		lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_ELS,
> +				 "2423 Unable to send mailbox");
> +		kfree(rdp_context);
> +		rjt_err = LSRJT_UNABLE_TPC;
> +		lpfc_nlp_put(ndlp);
> +		goto error;
> +	}
> +
> +	return 0;
> +rjt:
> +	rjt_err = LSRJT_CMD_UNSUPPORTED;
> +error:
> +	memset(&stat, 0, sizeof(stat));
> +	stat.un.b.lsRjtRsnCode = rjt_err;
> +	lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
> +	return 1;
> +}

Sebastian
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
James Smart May 20, 2015, 3:02 p.m. UTC | #3
Sebastian,

Agree with the statement on not returning right reject 
reasons/explanations. Patches will be reposted shortly with itcorrected 
as well as the spacing changes for this patch and patch 3.

-- james s


On 4/21/2015 8:38 AM, Sebastian Herbszt wrote:
> James Smart wrote:
>> Add support for RDP ELS command.
>>
>> ...
>> +/*
>> + * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS.
>> + * @vport: pointer to a host virtual N_Port data structure.
>> + * @cmdiocb: pointer to lpfc command iocb data structure.
>> + * @ndlp: pointer to a node-list data structure.
>> + *
>> + * This routine processes an unsolicited RDP(Read Diagnostic Parameters)
>> + * IOCB. First, the payload of the unsolicited RDP is checked.
>> + * Then it will (1) send MBX_DUMP_MEMORY, Embedded DMP_LMSD sub command TYPE-3
>> + * for Page A0, (2) send MBX_DUMP_MEMORY, DMP_LMSD for Page A2,
>> + * (3) send MBX_READ_LNK_STAT to get link stat, (4) Call lpfc_els_rdp_cmpl
>> + * gather all data and send RDP response.
>> + *
>> + * Return code
>> + *   0 - Sent the acc response
>> + *   1 - Sent the reject response.
>> + */
>> +static int
>> +lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
>> +		struct lpfc_nodelist *ndlp)
>> +{
>> +	struct lpfc_hba  *phba = vport->phba;
> here too
>
>> +	struct lpfc_dmabuf *pcmd;
>> +	uint8_t rjt_err;
>> +	struct fc_rdp_req_frame *rdp_req;
>> +	struct lpfc_rdp_context *rdp_context;
>> +	IOCB_t *cmd = NULL;
>> +	struct ls_rjt stat;
>> +
>> +	if (phba->sli_rev < LPFC_SLI_REV4 ||
>> +			(bf_get(lpfc_sli_intf_if_type,
>> +				&phba->sli4_hba.sli_intf) !=
>> +						LPFC_SLI_INTF_IF_TYPE_2)) {
>> +		rjt_err = LSRJT_CMD_UNSUPPORTED;
>> +		goto rjt;
>> +	}
>> +
>> +	if (phba->sli_rev < LPFC_SLI_REV4 || (phba->hba_flag & HBA_FCOE_MODE)) {
>> +		rjt_err = LSRJT_CMD_UNSUPPORTED;
>> +		goto rjt;
>> +	}
>> +
> Code at rjt label also sets rjt_err to LSRJT_CMD_UNSUPPORTED.
>
> Should the command not get rejected with LSRJT_UNABLE_TPC and
> LSEXP_REQ_UNSUPPORTED?
>
>> +	pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
>> +	rdp_req = (struct fc_rdp_req_frame *) pcmd->virt;
>> +
>> +
>> +	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
>> +			 "2422 ELS RDP Request "
>> +			 "dec len %d tag x%x port_id %d len %d\n",
>> +			 be32_to_cpu(rdp_req->rdp_des_length),
>> +			 be32_to_cpu(rdp_req->nport_id_desc.tag),
>> +			 be32_to_cpu(rdp_req->nport_id_desc.nport_id),
>> +			 be32_to_cpu(rdp_req->nport_id_desc.length));
>> +
>> +	if (sizeof(struct fc_rdp_nport_desc) !=
>> +			be32_to_cpu(rdp_req->rdp_des_length))
>> +		goto rjt;
>> +	if (RDP_N_PORT_DESC_TAG != be32_to_cpu(rdp_req->nport_id_desc.tag))
>> +		goto rjt;
>> +	if (RDP_NPORT_ID_SIZE !=
>> +			be32_to_cpu(rdp_req->nport_id_desc.length))
>> +		goto rjt;
>> +	rdp_context = kmalloc(sizeof(struct lpfc_rdp_context), GFP_KERNEL);
>> +	if (!rdp_context) {
>> +		rjt_err = LSRJT_UNABLE_TPC;
>> +		goto error;
>> +	}
>> +
>> +	memset(rdp_context, 0, sizeof(struct lpfc_rdp_context));
>> +	cmd = &cmdiocb->iocb;
>> +	rdp_context->ndlp = lpfc_nlp_get(ndlp);
>> +	rdp_context->ox_id = cmd->unsli3.rcvsli3.ox_id;
>> +	rdp_context->rx_id = cmd->ulpContext;
>> +	rdp_context->cmpl = lpfc_els_rdp_cmpl;
>> +	if (lpfc_get_rdp_info(phba, rdp_context)) {
>> +		lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_ELS,
>> +				 "2423 Unable to send mailbox");
>> +		kfree(rdp_context);
>> +		rjt_err = LSRJT_UNABLE_TPC;
>> +		lpfc_nlp_put(ndlp);
>> +		goto error;
>> +	}
>> +
>> +	return 0;
>> +rjt:
>> +	rjt_err = LSRJT_CMD_UNSUPPORTED;
>> +error:
>> +	memset(&stat, 0, sizeof(stat));
>> +	stat.un.b.lsRjtRsnCode = rjt_err;
>> +	lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
>> +	return 1;
>> +}
> Sebastian
>
>

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 3246d09..a5a56fa 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -231,6 +231,7 @@  struct lpfc_stats {
 	uint32_t elsRcvRTV;
 	uint32_t elsRcvECHO;
 	uint32_t elsRcvLCB;
+	uint32_t elsRcvRDP;
 	uint32_t elsXmitFLOGI;
 	uint32_t elsXmitFDISC;
 	uint32_t elsXmitPLOGI;
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 587e3e9..b0e6fe4 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -498,3 +498,5 @@  bool lpfc_disable_oas_lun(struct lpfc_hba *, struct lpfc_name *,
 bool lpfc_find_next_oas_lun(struct lpfc_hba *, struct lpfc_name *,
 			    struct lpfc_name *, uint64_t *, struct lpfc_name *,
 			    struct lpfc_name *, uint64_t *, uint32_t *);
+int lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox);
+void lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb);
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 9099a06..455a5dd 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -4617,6 +4617,417 @@  lpfc_els_disc_plogi(struct lpfc_vport *vport)
 	return sentplogi;
 }
 
+void
+lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc,
+		uint32_t word0)
+{
+
+	desc->tag = cpu_to_be32(RDP_LINK_SERVICE_DESC_TAG);
+	desc->payload.els_req = word0;
+	desc->length = cpu_to_be32(sizeof(desc->payload));
+}
+
+void
+lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc,
+		uint8_t *page_a0, uint8_t *page_a2)
+{
+	uint16_t wavelength;
+	uint16_t temperature;
+	uint16_t rx_power;
+	uint16_t tx_bias;
+	uint16_t tx_power;
+	uint16_t vcc;
+	uint16_t flag = 0;
+	struct sff_trasnceiver_codes_byte4 *trasn_code_byte4;
+	struct sff_trasnceiver_codes_byte5 *trasn_code_byte5;
+
+	desc->tag = cpu_to_be32(RDP_SFP_DESC_TAG);
+
+	trasn_code_byte4 = (struct sff_trasnceiver_codes_byte4 *)
+			&page_a0[SSF_TRANSCEIVER_CODE_B4];
+	trasn_code_byte5 = (struct sff_trasnceiver_codes_byte5 *)
+			&page_a0[SSF_TRANSCEIVER_CODE_B5];
+
+	if ((trasn_code_byte4->fc_sw_laser) ||
+	    (trasn_code_byte5->fc_sw_laser_sl) ||
+	    (trasn_code_byte5->fc_sw_laser_sn)) {  /* check if its short WL */
+		flag |= (SFP_FLAG_PT_SWLASER << SFP_FLAG_PT_SHIFT);
+	} else if (trasn_code_byte4->fc_lw_laser) {
+		wavelength = (page_a0[SSF_WAVELENGTH_B1] << 8) |
+			page_a0[SSF_WAVELENGTH_B0];
+		if (wavelength == SFP_WAVELENGTH_LC1310)
+			flag |= SFP_FLAG_PT_LWLASER_LC1310 << SFP_FLAG_PT_SHIFT;
+		if (wavelength == SFP_WAVELENGTH_LL1550)
+			flag |= SFP_FLAG_PT_LWLASER_LL1550 << SFP_FLAG_PT_SHIFT;
+	}
+	/* check if its SFP+ */
+	flag |= ((page_a0[SSF_IDENTIFIER] == SFF_PG0_IDENT_SFP) ?
+			SFP_FLAG_CT_SFP_PLUS : SFP_FLAG_CT_UNKNOWN)
+					<< SFP_FLAG_CT_SHIFT;
+
+	/* check if its OPTICAL */
+	flag |= ((page_a0[SSF_CONNECTOR] == SFF_PG0_CONNECTOR_LC) ?
+			SFP_FLAG_IS_OPTICAL_PORT : 0)
+					<< SFP_FLAG_IS_OPTICAL_SHIFT;
+
+	temperature = (page_a2[SFF_TEMPERATURE_B1] << 8 |
+		page_a2[SFF_TEMPERATURE_B0]);
+	vcc = (page_a2[SFF_VCC_B1] << 8 |
+		page_a2[SFF_VCC_B0]);
+	tx_power = (page_a2[SFF_TXPOWER_B1] << 8 |
+		page_a2[SFF_TXPOWER_B0]);
+	tx_bias = (page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 |
+		page_a2[SFF_TX_BIAS_CURRENT_B0]);
+	rx_power = (page_a2[SFF_RXPOWER_B1] << 8 |
+		page_a2[SFF_RXPOWER_B0]);
+	desc->sfp_info.temperature = cpu_to_be16(temperature);
+	desc->sfp_info.rx_power = cpu_to_be16(rx_power);
+	desc->sfp_info.tx_bias = cpu_to_be16(tx_bias);
+	desc->sfp_info.tx_power = cpu_to_be16(tx_power);
+	desc->sfp_info.vcc = cpu_to_be16(vcc);
+
+	desc->sfp_info.flags = cpu_to_be16(flag);
+	desc->length = cpu_to_be32(sizeof(desc->sfp_info));
+}
+
+void
+lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc,
+		READ_LNK_VAR *stat)
+{
+	uint32_t type;
+
+	desc->tag = cpu_to_be32(RDP_LINK_ERROR_STATUS_DESC_TAG);
+
+	type = VN_PT_PHY_PF_PORT << VN_PT_PHY_SHIFT;
+
+	desc->info.port_type = cpu_to_be32(type);
+
+	desc->info.link_status.link_failure_cnt =
+		cpu_to_be32(stat->linkFailureCnt);
+	desc->info.link_status.loss_of_synch_cnt =
+		cpu_to_be32(stat->lossSyncCnt);
+	desc->info.link_status.loss_of_signal_cnt =
+		cpu_to_be32(stat->lossSignalCnt);
+	desc->info.link_status.primitive_seq_proto_err =
+		cpu_to_be32(stat->primSeqErrCnt);
+	desc->info.link_status.invalid_trans_word =
+		cpu_to_be32(stat->invalidXmitWord);
+	desc->info.link_status.invalid_crc_cnt = cpu_to_be32(stat->crcCnt);
+
+	desc->length = cpu_to_be32(sizeof(desc->info));
+}
+
+void
+lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
+{
+	uint16_t rdp_cap = 0;
+	uint16_t rdp_speed;
+
+	desc->tag = cpu_to_be32(RDP_PORT_SPEED_DESC_TAG);
+
+	switch (phba->sli4_hba.link_state.speed) {
+	case LPFC_FC_LA_SPEED_1G:
+		rdp_speed = RDP_PS_1GB;
+		break;
+	case LPFC_FC_LA_SPEED_2G:
+		rdp_speed = RDP_PS_2GB;
+		break;
+	case LPFC_FC_LA_SPEED_4G:
+		rdp_speed  = RDP_PS_4GB;
+		break;
+	case LPFC_FC_LA_SPEED_8G:
+		rdp_speed = RDP_PS_8GB;
+		break;
+	case LPFC_FC_LA_SPEED_10G:
+		rdp_speed = RDP_PS_10GB;
+		break;
+	case LPFC_FC_LA_SPEED_16G:
+		rdp_speed = RDP_PS_16GB;
+		break;
+	case LPFC_FC_LA_SPEED_32G:
+		rdp_speed = RDP_PS_32GB;
+		break;
+	default:
+		rdp_speed = RDP_PS_UNKNOWN;
+		break;
+	}
+
+	desc->info.port_speed.speed = cpu_to_be16(rdp_speed);
+
+	if (phba->lmt & LMT_16Gb)
+		rdp_cap |= RDP_PS_16GB;
+	if (phba->lmt & LMT_10Gb)
+		rdp_cap |= RDP_PS_10GB;
+	if (phba->lmt & LMT_8Gb)
+		rdp_cap |= RDP_PS_8GB;
+	if (phba->lmt & LMT_4Gb)
+		rdp_cap |= RDP_PS_4GB;
+	if (phba->lmt & LMT_2Gb)
+		rdp_cap |= RDP_PS_2GB;
+	if (phba->lmt & LMT_1Gb)
+		rdp_cap |= RDP_PS_1GB;
+
+	if (rdp_cap == 0)
+		rdp_cap = RDP_CAP_UNKNOWN;
+
+	desc->info.port_speed.capabilities = cpu_to_be16(rdp_cap);
+	desc->length = cpu_to_be32(sizeof(desc->info));
+}
+
+void
+lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc,
+		struct lpfc_hba  *phba)
+{
+
+	desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
+
+	memcpy(desc->port_names.wwnn, phba->wwnn,
+			sizeof(desc->port_names.wwnn));
+
+	memcpy(desc->port_names.wwpn, &phba->wwpn,
+			sizeof(desc->port_names.wwpn));
+
+	desc->length = cpu_to_be32(sizeof(desc->port_names));
+}
+
+void
+lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc,
+		struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+
+	desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
+	if (vport->fc_flag & FC_FABRIC) {
+		memcpy(desc->port_names.wwnn, &vport->fabric_nodename,
+				sizeof(desc->port_names.wwnn));
+
+		memcpy(desc->port_names.wwpn, &vport->fabric_portname,
+				sizeof(desc->port_names.wwpn));
+	} else {  /* Point to Point */
+		memcpy(desc->port_names.wwnn, &ndlp->nlp_nodename,
+				sizeof(desc->port_names.wwnn));
+
+		memcpy(desc->port_names.wwnn, &ndlp->nlp_portname,
+				sizeof(desc->port_names.wwpn));
+	}
+
+	desc->length = cpu_to_be32(sizeof(desc->port_names));
+}
+
+void
+lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context,
+		int status)
+{
+	struct lpfc_nodelist *ndlp = rdp_context->ndlp;
+	struct lpfc_vport *vport = ndlp->vport;
+	struct lpfc_iocbq *elsiocb;
+	IOCB_t *icmd;
+	uint8_t *pcmd;
+	struct ls_rjt *stat;
+	struct fc_rdp_res_frame *rdp_res;
+	uint32_t cmdsize;
+	int rc;
+
+	if (status != SUCCESS)
+		goto error;
+	cmdsize = sizeof(struct fc_rdp_res_frame);
+
+	elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize,
+			lpfc_max_els_tries, rdp_context->ndlp,
+			rdp_context->ndlp->nlp_DID, ELS_CMD_ACC);
+	lpfc_nlp_put(ndlp);
+	if (!elsiocb)
+		goto free_rdp_context;
+
+	icmd = &elsiocb->iocb;
+	icmd->ulpContext = rdp_context->rx_id;
+	icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
+
+	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+			"2171 Xmit RDP response tag x%x xri x%x, "
+			"did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x",
+			elsiocb->iotag, elsiocb->iocb.ulpContext,
+			ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+			ndlp->nlp_rpi);
+	rdp_res = (struct fc_rdp_res_frame *)
+		(((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+	pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+	memset(pcmd, 0, sizeof(struct fc_rdp_res_frame));
+	*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+
+	/* For RDP payload */
+	lpfc_rdp_res_link_service(&rdp_res->link_service_desc, ELS_CMD_RDP);
+
+	lpfc_rdp_res_sfp_desc(&rdp_res->sfp_desc,
+			rdp_context->page_a0, rdp_context->page_a2);
+	lpfc_rdp_res_speed(&rdp_res->portspeed_desc, phba);
+	lpfc_rdp_res_link_error(&rdp_res->link_error_desc,
+			&rdp_context->link_stat);
+	lpfc_rdp_res_diag_port_names(&rdp_res->diag_port_names_desc, phba);
+	lpfc_rdp_res_attach_port_names(&rdp_res->attached_port_names_desc,
+			vport, ndlp);
+	rdp_res->length = cpu_to_be32(RDP_DESC_PAYLOAD_SIZE);
+
+	elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+
+	phba->fc_stat.elsXmitACC++;
+	rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+	if (rc == IOCB_ERROR)
+		lpfc_els_free_iocb(phba, elsiocb);
+
+	kfree(rdp_context);
+
+	return;
+error:
+	cmdsize = 2 * sizeof(uint32_t);
+	elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, lpfc_max_els_tries,
+			ndlp, ndlp->nlp_DID, ELS_CMD_LS_RJT);
+	lpfc_nlp_put(ndlp);
+	if (!elsiocb)
+		goto free_rdp_context;
+
+	icmd = &elsiocb->iocb;
+	icmd->ulpContext = rdp_context->rx_id;
+	icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
+	pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+	*((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
+	stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t));
+	stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+
+	phba->fc_stat.elsXmitLSRJT++;
+	elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+	rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+
+	if (rc == IOCB_ERROR)
+		lpfc_els_free_iocb(phba, elsiocb);
+free_rdp_context:
+	kfree(rdp_context);
+}
+
+int
+lpfc_get_rdp_info(struct lpfc_hba  *phba, struct lpfc_rdp_context *rdp_context)
+{
+	LPFC_MBOXQ_t *mbox = NULL;
+	int rc;
+
+	mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+	if (!mbox) {
+		lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS,
+				"7105 failed to allocate mailbox memory");
+		return 1;
+	}
+
+	if (lpfc_sli4_dump_page_a0(phba, mbox))
+		goto prep_mbox_fail;
+	mbox->vport = rdp_context->ndlp->vport;
+	mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a0;
+	mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+	rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+	if (rc == MBX_NOT_FINISHED)
+		goto issue_mbox_fail;
+
+	return 0;
+
+prep_mbox_fail:
+issue_mbox_fail:
+	mempool_free(mbox, phba->mbox_mem_pool);
+	return 1;
+}
+
+/*
+ * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes an unsolicited RDP(Read Diagnostic Parameters)
+ * IOCB. First, the payload of the unsolicited RDP is checked.
+ * Then it will (1) send MBX_DUMP_MEMORY, Embedded DMP_LMSD sub command TYPE-3
+ * for Page A0, (2) send MBX_DUMP_MEMORY, DMP_LMSD for Page A2,
+ * (3) send MBX_READ_LNK_STAT to get link stat, (4) Call lpfc_els_rdp_cmpl
+ * gather all data and send RDP response.
+ *
+ * Return code
+ *   0 - Sent the acc response
+ *   1 - Sent the reject response.
+ */
+static int
+lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+		struct lpfc_nodelist *ndlp)
+{
+	struct lpfc_hba  *phba = vport->phba;
+	struct lpfc_dmabuf *pcmd;
+	uint8_t rjt_err;
+	struct fc_rdp_req_frame *rdp_req;
+	struct lpfc_rdp_context *rdp_context;
+	IOCB_t *cmd = NULL;
+	struct ls_rjt stat;
+
+	if (phba->sli_rev < LPFC_SLI_REV4 ||
+			(bf_get(lpfc_sli_intf_if_type,
+				&phba->sli4_hba.sli_intf) !=
+						LPFC_SLI_INTF_IF_TYPE_2)) {
+		rjt_err = LSRJT_CMD_UNSUPPORTED;
+		goto rjt;
+	}
+
+	if (phba->sli_rev < LPFC_SLI_REV4 || (phba->hba_flag & HBA_FCOE_MODE)) {
+		rjt_err = LSRJT_CMD_UNSUPPORTED;
+		goto rjt;
+	}
+
+	pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+	rdp_req = (struct fc_rdp_req_frame *) pcmd->virt;
+
+
+	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+			 "2422 ELS RDP Request "
+			 "dec len %d tag x%x port_id %d len %d\n",
+			 be32_to_cpu(rdp_req->rdp_des_length),
+			 be32_to_cpu(rdp_req->nport_id_desc.tag),
+			 be32_to_cpu(rdp_req->nport_id_desc.nport_id),
+			 be32_to_cpu(rdp_req->nport_id_desc.length));
+
+	if (sizeof(struct fc_rdp_nport_desc) !=
+			be32_to_cpu(rdp_req->rdp_des_length))
+		goto rjt;
+	if (RDP_N_PORT_DESC_TAG != be32_to_cpu(rdp_req->nport_id_desc.tag))
+		goto rjt;
+	if (RDP_NPORT_ID_SIZE !=
+			be32_to_cpu(rdp_req->nport_id_desc.length))
+		goto rjt;
+	rdp_context = kmalloc(sizeof(struct lpfc_rdp_context), GFP_KERNEL);
+	if (!rdp_context) {
+		rjt_err = LSRJT_UNABLE_TPC;
+		goto error;
+	}
+
+	memset(rdp_context, 0, sizeof(struct lpfc_rdp_context));
+	cmd = &cmdiocb->iocb;
+	rdp_context->ndlp = lpfc_nlp_get(ndlp);
+	rdp_context->ox_id = cmd->unsli3.rcvsli3.ox_id;
+	rdp_context->rx_id = cmd->ulpContext;
+	rdp_context->cmpl = lpfc_els_rdp_cmpl;
+	if (lpfc_get_rdp_info(phba, rdp_context)) {
+		lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_ELS,
+				 "2423 Unable to send mailbox");
+		kfree(rdp_context);
+		rjt_err = LSRJT_UNABLE_TPC;
+		lpfc_nlp_put(ndlp);
+		goto error;
+	}
+
+	return 0;
+rjt:
+	rjt_err = LSRJT_CMD_UNSUPPORTED;
+error:
+	memset(&stat, 0, sizeof(stat));
+	stat.un.b.lsRjtRsnCode = rjt_err;
+	lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+	return 1;
+}
+
+
 static void
 lpfc_els_lcb_rsp(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
 {
@@ -7058,6 +7469,10 @@  lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 		phba->fc_stat.elsRcvLCB++;
 		lpfc_els_rcv_lcb(vport, elsiocb, ndlp);
 		break;
+	case ELS_CMD_RDP:
+		phba->fc_stat.elsRcvRDP++;
+		lpfc_els_rcv_rdp(vport, elsiocb, ndlp);
+		break;
 	case ELS_CMD_RSCN:
 		phba->fc_stat.elsRcvRSCN++;
 		lpfc_els_rcv_rscn(vport, elsiocb, ndlp);
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 6ad0a6f..892c525 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -543,6 +543,7 @@  struct fc_vft_header {
 #define ELS_CMD_TEST      0x11000000
 #define ELS_CMD_RRQ       0x12000000
 #define ELS_CMD_REC       0x13000000
+#define ELS_CMD_RDP       0x18000000
 #define ELS_CMD_PRLI      0x20100014
 #define ELS_CMD_PRLO      0x21100014
 #define ELS_CMD_PRLO_ACC  0x02100014
@@ -581,6 +582,7 @@  struct fc_vft_header {
 #define ELS_CMD_TEST      0x11
 #define ELS_CMD_RRQ       0x12
 #define ELS_CMD_REC       0x13
+#define ELS_CMD_RDP	  0x18
 #define ELS_CMD_PRLI      0x14001020
 #define ELS_CMD_PRLO      0x14001021
 #define ELS_CMD_PRLO_ACC  0x14001002
@@ -1042,6 +1044,168 @@  struct fc_lcb_res_frame {
 	uint16_t      lcb_duration;     /* LCB Payload Word 2, bit 15:0  */
 };
 
+/*
+ * Read Diagnostic Parameters (RDP) ELS frame.
+ */
+#define SFF_PG0_IDENT_SFP              0x3
+
+#define SFP_FLAG_PT_OPTICAL            0x0
+#define SFP_FLAG_PT_SWLASER            0x01
+#define SFP_FLAG_PT_LWLASER_LC1310     0x02
+#define SFP_FLAG_PT_LWLASER_LL1550     0x03
+#define SFP_FLAG_PT_MASK               0x0F
+#define SFP_FLAG_PT_SHIFT              0
+
+#define SFP_FLAG_IS_OPTICAL_PORT       0x01
+#define SFP_FLAG_IS_OPTICAL_MASK       0x010
+#define SFP_FLAG_IS_OPTICAL_SHIFT      4
+
+#define SFP_FLAG_IS_DESC_VALID         0x01
+#define SFP_FLAG_IS_DESC_VALID_MASK    0x020
+#define SFP_FLAG_IS_DESC_VALID_SHIFT   5
+
+#define SFP_FLAG_CT_UNKNOWN            0x0
+#define SFP_FLAG_CT_SFP_PLUS           0x01
+#define SFP_FLAG_CT_MASK               0x3C
+#define SFP_FLAG_CT_SHIFT              6
+
+struct fc_rdp_port_name_info {
+	uint8_t wwnn[8];
+	uint8_t wwpn[8];
+};
+
+
+/*
+ * Link Error Status Block Structure (FC-FS-3) for RDP
+ * This similar to RPS ELS
+ */
+struct fc_link_status {
+	uint32_t      link_failure_cnt;
+	uint32_t      loss_of_synch_cnt;
+	uint32_t      loss_of_signal_cnt;
+	uint32_t      primitive_seq_proto_err;
+	uint32_t      invalid_trans_word;
+	uint32_t      invalid_crc_cnt;
+
+};
+
+#define RDP_PORT_NAMES_DESC_TAG  0x00010003
+struct fc_rdp_port_name_desc {
+	uint32_t	tag;     /* 0001 0003h */
+	uint32_t	length;  /* set to size of payload struct */
+	struct fc_rdp_port_name_info  port_names;
+};
+
+
+struct fc_rdp_link_error_status_payload_info {
+	struct fc_link_status link_status; /* 24 bytes */
+	uint32_t  port_type;             /* bits 31-30 only */
+};
+
+#define RDP_LINK_ERROR_STATUS_DESC_TAG  0x00010002
+struct fc_rdp_link_error_status_desc {
+	uint32_t         tag;     /* 0001 0002h */
+	uint32_t         length;  /* set to size of payload struct */
+	struct fc_rdp_link_error_status_payload_info info;
+};
+
+#define VN_PT_PHY_UNKNOWN      0x00
+#define VN_PT_PHY_PF_PORT      0x01
+#define VN_PT_PHY_ETH_MAC      0x10
+#define VN_PT_PHY_SHIFT                30
+
+#define RDP_PS_1GB             0x8000
+#define RDP_PS_2GB             0x4000
+#define RDP_PS_4GB             0x2000
+#define RDP_PS_10GB            0x1000
+#define RDP_PS_8GB             0x0800
+#define RDP_PS_16GB            0x0400
+#define RDP_PS_32GB            0x0200
+
+#define RDP_CAP_UNKNOWN        0x0001
+#define RDP_PS_UNKNOWN         0x0002
+#define RDP_PS_NOT_ESTABLISHED 0x0001
+
+struct fc_rdp_port_speed {
+	uint16_t   capabilities;
+	uint16_t   speed;
+};
+
+struct fc_rdp_port_speed_info {
+	struct fc_rdp_port_speed   port_speed;
+};
+
+#define RDP_PORT_SPEED_DESC_TAG  0x00010001
+struct fc_rdp_port_speed_desc {
+	uint32_t         tag;            /* 00010001h */
+	uint32_t         length;         /* set to size of payload struct */
+	struct fc_rdp_port_speed_info info;
+};
+
+#define RDP_NPORT_ID_SIZE      4
+#define RDP_N_PORT_DESC_TAG    0x00000003
+struct fc_rdp_nport_desc {
+	uint32_t         tag;          /* 0000 0003h, big endian */
+	uint32_t         length;       /* size of RDP_N_PORT_ID struct */
+	uint32_t         nport_id : 12;
+	uint32_t         reserved : 8;
+};
+
+
+struct fc_rdp_link_service_info {
+	uint32_t         els_req;    /* Request payload word 0 value.*/
+};
+
+#define RDP_LINK_SERVICE_DESC_TAG  0x00000001
+struct fc_rdp_link_service_desc {
+	uint32_t         tag;     /* Descriptor tag  1 */
+	uint32_t         length;  /* set to size of payload struct. */
+	struct fc_rdp_link_service_info  payload;
+				  /* must be ELS req Word 0(0x18) */
+};
+
+struct fc_rdp_sfp_info {
+	uint16_t	temperature;
+	uint16_t	vcc;
+	uint16_t	tx_bias;
+	uint16_t	tx_power;
+	uint16_t	rx_power;
+	uint16_t	flags;
+};
+
+#define RDP_SFP_DESC_TAG  0x00010000
+struct fc_rdp_sfp_desc {
+	uint32_t         tag;
+	uint32_t         length;  /* set to size of sfp_info struct */
+	struct fc_rdp_sfp_info sfp_info;
+};
+
+struct fc_rdp_req_frame {
+	uint32_t         rdp_command;           /* ELS command opcode (0x18)*/
+	uint32_t         rdp_des_length;        /* RDP Payload Word 1 */
+	struct fc_rdp_nport_desc nport_id_desc; /* RDP Payload Word 2 - 4 */
+};
+
+
+struct fc_rdp_res_frame {
+	uint32_t	reply_sequence;		/* FC word0 LS_ACC or LS_RJT */
+	uint32_t	length;			/* FC Word 1      */
+	struct fc_rdp_link_service_desc link_service_desc;    /* Word 2 -4  */
+	struct fc_rdp_sfp_desc sfp_desc;                      /* Word 5 -9  */
+	struct fc_rdp_port_speed_desc portspeed_desc;         /* Word 10-12 */
+	struct fc_rdp_link_error_status_desc link_error_desc; /* Word 13-21 */
+	struct fc_rdp_port_name_desc diag_port_names_desc;    /* Word 22-27 */
+	struct fc_rdp_port_name_desc attached_port_names_desc;/* Word 28-33 */
+};
+
+
+#define RDP_DESC_PAYLOAD_SIZE (sizeof(struct fc_rdp_link_service_desc) \
+			+ sizeof(struct fc_rdp_sfp_desc) \
+			+ sizeof(struct fc_rdp_port_speed_desc) \
+			+ sizeof(struct fc_rdp_link_error_status_desc) \
+			+ (sizeof(struct fc_rdp_port_name_desc) * 2))
+
+
 /******** FDMI ********/
 
 /* lpfc_sli_ct_request defines the CT_IU preamble for FDMI commands */
@@ -1619,6 +1783,11 @@  typedef struct {		/* FireFly BIU registers */
 #define TEMPERATURE_OFFSET 0xB0	/* Slim offset for critical temperature event */
 
 /*
+ * return code Fail
+ */
+#define FAILURE 1
+
+/*
  *    Begin Structure Definitions for Mailbox Commands
  */
 
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 9f2bfcc..7cc7bec 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -2455,6 +2455,205 @@  struct lpfc_mbx_supp_pages {
 #define LPFC_SLI4_PARAMETERS		2
 };
 
+struct lpfc_mbx_memory_dump_type3 {
+	uint32_t word1;
+#define lpfc_mbx_memory_dump_type3_type_SHIFT    0
+#define lpfc_mbx_memory_dump_type3_type_MASK     0x0000000f
+#define lpfc_mbx_memory_dump_type3_type_WORD     word1
+#define lpfc_mbx_memory_dump_type3_link_SHIFT    24
+#define lpfc_mbx_memory_dump_type3_link_MASK     0x000000ff
+#define lpfc_mbx_memory_dump_type3_link_WORD     word1
+	uint32_t word2;
+#define lpfc_mbx_memory_dump_type3_page_no_SHIFT  0
+#define lpfc_mbx_memory_dump_type3_page_no_MASK   0x0000ffff
+#define lpfc_mbx_memory_dump_type3_page_no_WORD   word2
+#define lpfc_mbx_memory_dump_type3_offset_SHIFT   16
+#define lpfc_mbx_memory_dump_type3_offset_MASK    0x0000ffff
+#define lpfc_mbx_memory_dump_type3_offset_WORD    word2
+	uint32_t word3;
+#define lpfc_mbx_memory_dump_type3_length_SHIFT  0
+#define lpfc_mbx_memory_dump_type3_length_MASK   0x00ffffff
+#define lpfc_mbx_memory_dump_type3_length_WORD   word3
+	uint32_t addr_lo;
+	uint32_t addr_hi;
+	uint32_t return_len;
+};
+
+#define DMP_PAGE_A0             0xa0
+#define DMP_PAGE_A2             0xa2
+#define DMP_SFF_PAGE_A0_SIZE	256
+#define DMP_SFF_PAGE_A2_SIZE	256
+
+#define SFP_WAVELENGTH_LC1310	1310
+#define SFP_WAVELENGTH_LL1550	1550
+
+
+/*
+ *  * SFF-8472 TABLE 3.4
+ *   */
+#define  SFF_PG0_CONNECTOR_UNKNOWN    0x00   /* Unknown  */
+#define  SFF_PG0_CONNECTOR_SC         0x01   /* SC       */
+#define  SFF_PG0_CONNECTOR_FC_COPPER1 0x02   /* FC style 1 copper connector */
+#define  SFF_PG0_CONNECTOR_FC_COPPER2 0x03   /* FC style 2 copper connector */
+#define  SFF_PG0_CONNECTOR_BNC        0x04   /* BNC / TNC */
+#define  SFF_PG0_CONNECTOR__FC_COAX   0x05   /* FC coaxial headers */
+#define  SFF_PG0_CONNECTOR_FIBERJACK  0x06   /* FiberJack */
+#define  SFF_PG0_CONNECTOR_LC         0x07   /* LC        */
+#define  SFF_PG0_CONNECTOR_MT         0x08   /* MT - RJ   */
+#define  SFF_PG0_CONNECTOR_MU         0x09   /* MU        */
+#define  SFF_PG0_CONNECTOR_SF         0x0A   /* SG        */
+#define  SFF_PG0_CONNECTOR_OPTICAL_PIGTAIL 0x0B /* Optical pigtail */
+#define  SFF_PG0_CONNECTOR_OPTICAL_PARALLEL 0x0C /* MPO Parallel Optic */
+#define  SFF_PG0_CONNECTOR_HSSDC_II   0x20   /* HSSDC II */
+#define  SFF_PG0_CONNECTOR_COPPER_PIGTAIL 0x21 /* Copper pigtail */
+#define  SFF_PG0_CONNECTOR_RJ45       0x22  /* RJ45 */
+
+/* SFF-8472 Table 3.1 Diagnostics: Data Fields Address/Page A0 */
+
+#define SSF_IDENTIFIER			0
+#define SSF_EXT_IDENTIFIER		1
+#define SSF_CONNECTOR			2
+#define SSF_TRANSCEIVER_CODE_B0		3
+#define SSF_TRANSCEIVER_CODE_B1		4
+#define SSF_TRANSCEIVER_CODE_B2		5
+#define SSF_TRANSCEIVER_CODE_B3		6
+#define SSF_TRANSCEIVER_CODE_B4		7
+#define SSF_TRANSCEIVER_CODE_B5		8
+#define SSF_TRANSCEIVER_CODE_B6		9
+#define SSF_TRANSCEIVER_CODE_B7		10
+#define SSF_ENCODING			11
+#define SSF_BR_NOMINAL			12
+#define SSF_RATE_IDENTIFIER		13
+#define SSF_LENGTH_9UM_KM		14
+#define SSF_LENGTH_9UM			15
+#define SSF_LENGTH_50UM_OM2		16
+#define SSF_LENGTH_62UM_OM1		17
+#define SFF_LENGTH_COPPER		18
+#define SSF_LENGTH_50UM_OM3		19
+#define SSF_VENDOR_NAME			20
+#define SSF_VENDOR_OUI			36
+#define SSF_VENDOR_PN			40
+#define SSF_VENDOR_REV			56
+#define SSF_WAVELENGTH_B1		60
+#define SSF_WAVELENGTH_B0		61
+#define SSF_CC_BASE			63
+#define SSF_OPTIONS_B1			64
+#define SSF_OPTIONS_B0			65
+#define SSF_BR_MAX			66
+#define SSF_BR_MIN			67
+#define SSF_VENDOR_SN			68
+#define SSF_DATE_CODE			84
+#define SSF_MONITORING_TYPEDIAGNOSTIC	92
+#define SSF_ENHANCED_OPTIONS		93
+#define SFF_8472_COMPLIANCE		94
+#define SSF_CC_EXT			95
+#define SSF_A0_VENDOR_SPECIFIC		96
+
+/* SFF-8472 Table 3.1a Diagnostics: Data Fields Address/Page A2 */
+
+#define SSF_AW_THRESHOLDS		0
+#define SSF_EXT_CAL_CONSTANTS		56
+#define SSF_CC_DMI			95
+#define SFF_TEMPERATURE_B1		96
+#define SFF_TEMPERATURE_B0		97
+#define SFF_VCC_B1			98
+#define SFF_VCC_B0			99
+#define SFF_TX_BIAS_CURRENT_B1		100
+#define SFF_TX_BIAS_CURRENT_B0		101
+#define SFF_TXPOWER_B1			102
+#define SFF_TXPOWER_B0			103
+#define SFF_RXPOWER_B1			104
+#define SFF_RXPOWER_B0			105
+#define SSF_STATUS_CONTROL		110
+#define SSF_ALARM_FLAGS_B1		112
+#define SSF_ALARM_FLAGS_B0		113
+#define SSF_WARNING_FLAGS_B1		116
+#define SSF_WARNING_FLAGS_B0		117
+#define SSF_EXT_TATUS_CONTROL_B1	118
+#define SSF_EXT_TATUS_CONTROL_B0	119
+#define SSF_A2_VENDOR_SPECIFIC		120
+#define SSF_USER_EEPROM			128
+#define SSF_VENDOR_CONTROL		148
+
+
+/*
+ * Tranceiver codes Fibre Channel SFF-8472
+ * Table 3.5.
+ */
+
+struct sff_trasnceiver_codes_byte0 {
+	uint8_t inifiband:4;
+	uint8_t teng_ethernet:4;
+};
+
+struct sff_trasnceiver_codes_byte1 {
+	uint8_t  sonet:6;
+	uint8_t  escon:2;
+};
+
+struct sff_trasnceiver_codes_byte2 {
+	uint8_t  soNet:8;
+};
+
+struct sff_trasnceiver_codes_byte3 {
+	uint8_t ethernet:8;
+};
+
+struct sff_trasnceiver_codes_byte4 {
+	uint8_t fc_el_lo:1;
+	uint8_t fc_lw_laser:1;
+	uint8_t fc_sw_laser:1;
+	uint8_t fc_md_distance:1;
+	uint8_t fc_lg_distance:1;
+	uint8_t fc_int_distance:1;
+	uint8_t fc_short_distance:1;
+	uint8_t fc_vld_distance:1;
+};
+
+struct sff_trasnceiver_codes_byte5 {
+	uint8_t reserved1:1;
+	uint8_t reserved2:1;
+	uint8_t fc_sfp_active:1;  /* Active cable   */
+	uint8_t fc_sfp_passive:1; /* Passive cable  */
+	uint8_t fc_lw_laser:1;     /* Longwave laser */
+	uint8_t fc_sw_laser_sl:1;
+	uint8_t fc_sw_laser_sn:1;
+	uint8_t fc_el_hi:1;        /* Electrical enclosure high bit */
+};
+
+struct sff_trasnceiver_codes_byte6 {
+	uint8_t fc_tm_sm:1;      /* Single Mode */
+	uint8_t reserved:1;
+	uint8_t fc_tm_m6:1;       /* Multimode, 62.5um (M6) */
+	uint8_t fc_tm_tv:1;      /* Video Coax (TV) */
+	uint8_t fc_tm_mi:1;      /* Miniature Coax (MI) */
+	uint8_t fc_tm_tp:1;      /* Twisted Pair (TP) */
+	uint8_t fc_tm_tw:1;      /* Twin Axial Pair  */
+};
+
+struct sff_trasnceiver_codes_byte7 {
+	uint8_t fc_sp_100MB:1;   /*  100 MB/sec */
+	uint8_t reserve:1;
+	uint8_t fc_sp_200mb:1;   /*  200 MB/sec */
+	uint8_t fc_sp_3200MB:1;  /* 3200 MB/sec */
+	uint8_t fc_sp_400MB:1;   /*  400 MB/sec */
+	uint8_t fc_sp_1600MB:1;  /* 1600 MB/sec */
+	uint8_t fc_sp_800MB:1;   /*  800 MB/sec */
+	uint8_t fc_sp_1200MB:1;  /* 1200 MB/sec */
+};
+
+/* User writable non-volatile memory, SFF-8472 Table 3.20 */
+struct user_eeprom {
+	uint8_t vendor_name[16];
+	uint8_t vendor_oui[3];
+	uint8_t vendor_pn[816];
+	uint8_t vendor_rev[4];
+	uint8_t vendor_sn[16];
+	uint8_t datecode[6];
+	uint8_t lot_code[2];
+	uint8_t reserved191[57];
+};
+
 struct lpfc_mbx_pc_sli4_params {
 	uint32_t word1;
 #define qs_SHIFT				0
@@ -3054,6 +3253,7 @@  struct lpfc_mqe {
 		struct lpfc_mbx_get_prof_cfg get_prof_cfg;
 		struct lpfc_mbx_wr_object wr_object;
 		struct lpfc_mbx_get_port_name get_port_name;
+		struct lpfc_mbx_memory_dump_type3 mem_dump_type3;
 		struct lpfc_mbx_nop nop;
 	} un;
 };
@@ -3199,6 +3399,7 @@  struct lpfc_acqe_fc_la {
 #define LPFC_FC_LA_SPEED_8G		0x8
 #define LPFC_FC_LA_SPEED_10G		0xA
 #define LPFC_FC_LA_SPEED_16G		0x10
+#define LPFC_FC_LA_SPEED_32G            0x20
 #define lpfc_acqe_fc_la_topology_SHIFT		16
 #define lpfc_acqe_fc_la_topology_MASK		0x000000FF
 #define lpfc_acqe_fc_la_topology_WORD		word0
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 816f596..eb62772 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -2255,6 +2255,158 @@  lpfc_sli4_dump_cfg_rg23(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
 	return 0;
 }
 
+void
+lpfc_mbx_cmpl_rdp_link_stat(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+	MAILBOX_t *mb;
+	int rc = FAILURE;
+	struct lpfc_rdp_context *rdp_context =
+			(struct lpfc_rdp_context *)(mboxq->context2);
+
+	mb = &mboxq->u.mb;
+	if (mb->mbxStatus)
+		goto mbx_failed;
+
+	memcpy(&rdp_context->link_stat, &mb->un.varRdLnk, sizeof(READ_LNK_VAR));
+
+	rc = SUCCESS;
+
+mbx_failed:
+	lpfc_sli4_mbox_cmd_free(phba, mboxq);
+	rdp_context->cmpl(phba, rdp_context, rc);
+}
+
+void
+lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+	struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) mbox->context1;
+	struct lpfc_rdp_context *rdp_context =
+			(struct lpfc_rdp_context *)(mbox->context2);
+
+	if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
+		goto error;
+
+	lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2,
+				DMP_SFF_PAGE_A2_SIZE);
+
+	/* We don't need dma buffer for link stat. */
+	lpfc_mbuf_free(phba, mp->virt, mp->phys);
+	kfree(mp);
+
+	memset(mbox, 0, sizeof(*mbox));
+	lpfc_read_lnk_stat(phba, mbox);
+	mbox->vport = rdp_context->ndlp->vport;
+	mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_link_stat;
+	mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+	if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) == MBX_NOT_FINISHED)
+		goto error;
+
+	return;
+
+error:
+	lpfc_mbuf_free(phba, mp->virt, mp->phys);
+	kfree(mp);
+	lpfc_sli4_mbox_cmd_free(phba, mbox);
+	rdp_context->cmpl(phba, rdp_context, FAILURE);
+}
+
+void
+lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+	int rc;
+	struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (mbox->context1);
+	struct lpfc_rdp_context *rdp_context =
+			(struct lpfc_rdp_context *)(mbox->context2);
+
+	if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
+		goto error;
+
+	lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a0,
+				DMP_SFF_PAGE_A0_SIZE);
+
+	memset(mbox, 0, sizeof(*mbox));
+
+	memset(mp->virt, 0, DMP_SFF_PAGE_A2_SIZE);
+	INIT_LIST_HEAD(&mp->list);
+
+	/* save address for completion */
+	mbox->context1 = mp;
+	mbox->vport = rdp_context->ndlp->vport;
+
+	bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+	bf_set(lpfc_mbx_memory_dump_type3_type,
+		&mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+	bf_set(lpfc_mbx_memory_dump_type3_link,
+		&mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+	bf_set(lpfc_mbx_memory_dump_type3_page_no,
+		&mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A2);
+	bf_set(lpfc_mbx_memory_dump_type3_length,
+		&mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A2_SIZE);
+	mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+	mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+
+	mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a2;
+	mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+	rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+	if (rc == MBX_NOT_FINISHED)
+		goto error;
+
+	return;
+
+error:
+	lpfc_mbuf_free(phba, mp->virt, mp->phys);
+	kfree(mp);
+	lpfc_sli4_mbox_cmd_free(phba, mbox);
+	rdp_context->cmpl(phba, rdp_context, FAILURE);
+}
+
+
+/*
+ * lpfc_sli4_dump_sfp_pagea0 - Dump sli4 read SFP Diagnostic.
+ * @phba: pointer to the hba structure containing.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * This function create a SLI4 dump mailbox command to dump configure
+ * type 3 page 0xA0.
+ */
+int
+lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+	struct lpfc_dmabuf *mp = NULL;
+
+	memset(mbox, 0, sizeof(*mbox));
+
+	mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+	if (mp)
+		mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+	if (!mp || !mp->virt) {
+		kfree(mp);
+		lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+			"3569 dump type 3 page 0xA0 allocation failed\n");
+		return 1;
+	}
+
+	memset(mp->virt, 0, LPFC_BPL_SIZE);
+	INIT_LIST_HEAD(&mp->list);
+
+	bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+	/* save address for completion */
+	mbox->context1 = mp;
+
+	bf_set(lpfc_mbx_memory_dump_type3_type,
+		&mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+	bf_set(lpfc_mbx_memory_dump_type3_link,
+		&mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+	bf_set(lpfc_mbx_memory_dump_type3_page_no,
+		&mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A0);
+	bf_set(lpfc_mbx_memory_dump_type3_length,
+		&mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A0_SIZE);
+	mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+	mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+
+	return 0;
+}
+
 /**
  * lpfc_reg_fcfi - Initialize the REG_FCFI mailbox command
  * @phba: pointer to the hba structure containing the FCF index and RQ ID.
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 2ce6cb5..d1a5b05 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -652,6 +652,16 @@  struct lpfc_rsrc_blks {
 	uint16_t rsrc_used;
 };
 
+struct lpfc_rdp_context {
+	struct lpfc_nodelist *ndlp;
+	uint16_t ox_id;
+	uint16_t rx_id;
+	READ_LNK_VAR link_stat;
+	uint8_t page_a0[DMP_SFF_PAGE_A0_SIZE];
+	uint8_t page_a2[DMP_SFF_PAGE_A2_SIZE];
+	void (*cmpl)(struct lpfc_hba *, struct lpfc_rdp_context*, int);
+};
+
 struct lpfc_lcb_context {
 	uint8_t  sub_command;
 	uint8_t  type;