diff mbox series

[v5,29/31] elx: efct: scsi_transport_fc host interface support

Message ID 20210103171134.39878-30-jsmart2021@gmail.com
State Superseded
Headers show
Series efct: Broadcom (Emulex) FC Target driver | expand

Commit Message

James Smart Jan. 3, 2021, 5:11 p.m. UTC
This patch continues the efct driver population.

This patch adds driver definitions for:
Integration with the scsi_fc_transport host interfaces

Co-developed-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
---
 drivers/scsi/elx/efct/efct_xport.c | 500 +++++++++++++++++++++++++++++
 1 file changed, 500 insertions(+)

Comments

Daniel Wagner Jan. 4, 2021, 6:12 p.m. UTC | #1
On Sun, Jan 03, 2021 at 09:11:32AM -0800, James Smart wrote:
> This patch continues the efct driver population.

> 

> This patch adds driver definitions for:

> Integration with the scsi_fc_transport host interfaces

> 

> Co-developed-by: Ram Vegesna <ram.vegesna@broadcom.com>

> Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>

> Signed-off-by: James Smart <jsmart2021@gmail.com>


Reviewed-by: Daniel Wagner <dwagner@suse.de>
Hannes Reinecke March 5, 2021, 12:47 p.m. UTC | #2
On 1/3/21 6:11 PM, James Smart wrote:
> This patch continues the efct driver population.

> 

> This patch adds driver definitions for:

> Integration with the scsi_fc_transport host interfaces

> 

> Co-developed-by: Ram Vegesna <ram.vegesna@broadcom.com>

> Signed-off-by: Ram Vegesna <ram.vegesna@broadcom.com>

> Signed-off-by: James Smart <jsmart2021@gmail.com>

> ---

>   drivers/scsi/elx/efct/efct_xport.c | 500 +++++++++++++++++++++++++++++

>   1 file changed, 500 insertions(+)

> 

> diff --git a/drivers/scsi/elx/efct/efct_xport.c b/drivers/scsi/elx/efct/efct_xport.c

> index 0cf558c03547..bee13337afbc 100644

> --- a/drivers/scsi/elx/efct/efct_xport.c

> +++ b/drivers/scsi/elx/efct/efct_xport.c

> @@ -778,3 +778,503 @@ int efct_scsi_del_device(struct efct *efct)

>   	}

>   	return EFC_SUCCESS;

>   }

> +

> +static void

> +efct_get_host_port_id(struct Scsi_Host *shost)

> +{

> +	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;

> +	struct efct *efct = vport->efct;

> +	struct efc *efc = efct->efcport;

> +	struct efc_nport *nport;

> +

> +	if (efc->domain && efc->domain->nport) {

> +		nport = efc->domain->nport;

> +		fc_host_port_id(shost) = nport->fc_id;

> +	}

> +}

> +

> +static void

> +efct_get_host_port_type(struct Scsi_Host *shost)

> +{

> +	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;

> +	struct efct *efct = vport->efct;

> +	struct efc *efc = efct->efcport;

> +	int type = FC_PORTTYPE_UNKNOWN;

> +

> +	if (efc->domain && efc->domain->nport) {

> +		if (efc->domain->is_loop) {

> +			type = FC_PORTTYPE_LPORT;

> +		} else {

> +			struct efc_nport *nport = efc->domain->nport;

> +

> +			if (nport->is_vport)

> +				type = FC_PORTTYPE_NPIV;

> +			else if (nport->topology == EFC_NPORT_TOPO_P2P)

> +				type = FC_PORTTYPE_PTP;

> +			else if (nport->topology == EFC_NPORT_TOPO_UNKNOWN)

> +				type = FC_PORTTYPE_UNKNOWN;

> +			else

> +				type = FC_PORTTYPE_NPORT;

> +		}

> +	}

> +	fc_host_port_type(shost) = type;

> +}

> +

> +static void

> +efct_get_host_vport_type(struct Scsi_Host *shost)

> +{

> +	fc_host_port_type(shost) = FC_PORTTYPE_NPIV;

> +}

> +

> +static void

> +efct_get_host_port_state(struct Scsi_Host *shost)

> +{

> +	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;

> +	struct efct *efct = vport->efct;

> +	struct efc *efc = efct->efcport;

> +

> +	if (efc->domain)

> +		fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;

> +	else

> +		fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;

> +}


Doesn't this need some kind of synchronisation?
And just checking if the 'domain' pointer is present looks a tad off.
What abort 'blocked', which is typically set when RSCN are processed?
Or linkdown?

> +

> +static void

> +efct_get_host_speed(struct Scsi_Host *shost)

> +{

> +	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;

> +	struct efct *efct = vport->efct;

> +	struct efc *efc = efct->efcport;

> +	union efct_xport_stats_u speed;

> +	u32 fc_speed = FC_PORTSPEED_UNKNOWN;

> +	int rc;

> +

> +	if (!efc->domain || !efc->domain->nport) {

> +		fc_host_speed(shost) = fc_speed;

> +		return;

> +	}

> +

> +	rc = efct_xport_status(efct->xport, EFCT_XPORT_LINK_SPEED, &speed);

> +	if (rc == 0) {

> +		switch (speed.value) {

> +		case 1000:

> +			fc_speed = FC_PORTSPEED_1GBIT;

> +			break;

> +		case 2000:

> +			fc_speed = FC_PORTSPEED_2GBIT;

> +			break;

> +		case 4000:

> +			fc_speed = FC_PORTSPEED_4GBIT;

> +			break;

> +		case 8000:

> +			fc_speed = FC_PORTSPEED_8GBIT;

> +			break;

> +		case 10000:

> +			fc_speed = FC_PORTSPEED_10GBIT;

> +			break;

> +		case 16000:

> +			fc_speed = FC_PORTSPEED_16GBIT;

> +			break;

> +		case 32000:

> +			fc_speed = FC_PORTSPEED_32GBIT;

> +			break;

> +		}

> +	}

> +


Why not 64G and 128G?
You already have definitions for it, so why not put them in here?


> +	fc_host_speed(shost) = fc_speed;

> +}

> +

> +static void

> +efct_get_host_fabric_name(struct Scsi_Host *shost)

> +{

> +	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;

> +	struct efct *efct = vport->efct;

> +	struct efc *efc = efct->efcport;

> +

> +	if (efc->domain) {

> +		struct fc_els_flogi  *sp =

> +			(struct fc_els_flogi  *)

> +				efc->domain->flogi_service_params;

> +

> +		fc_host_fabric_name(shost) = be64_to_cpu(sp->fl_wwnn);

> +	}

> +}

> +

> +static struct fc_host_statistics *

> +efct_get_stats(struct Scsi_Host *shost)

> +{

> +	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;

> +	struct efct *efct = vport->efct;

> +	union efct_xport_stats_u stats;

> +	struct efct_xport *xport = efct->xport;

> +	u32 rc = 1;

> +

> +	rc = efct_xport_status(xport, EFCT_XPORT_LINK_STATISTICS, &stats);

> +	if (rc != 0) {

> +		pr_err("efct_xport_status returned non 0 - %d\n", rc);

> +		return NULL;

> +	}

> +

> +	vport->fc_host_stats.loss_of_sync_count =

> +		stats.stats.link_stats.loss_of_sync_error_count;

> +	vport->fc_host_stats.link_failure_count =

> +		stats.stats.link_stats.link_failure_error_count;

> +	vport->fc_host_stats.prim_seq_protocol_err_count =

> +		stats.stats.link_stats.primitive_sequence_error_count;

> +	vport->fc_host_stats.invalid_tx_word_count =

> +		stats.stats.link_stats.invalid_transmission_word_error_count;

> +	vport->fc_host_stats.invalid_crc_count =

> +		stats.stats.link_stats.crc_error_count;

> +	/* mbox returns kbyte count so we need to convert to words */

> +	vport->fc_host_stats.tx_words =

> +		stats.stats.host_stats.transmit_kbyte_count * 256;

> +	/* mbox returns kbyte count so we need to convert to words */

> +	vport->fc_host_stats.rx_words =

> +		stats.stats.host_stats.receive_kbyte_count * 256;

> +	vport->fc_host_stats.tx_frames =

> +		stats.stats.host_stats.transmit_frame_count;

> +	vport->fc_host_stats.rx_frames =

> +		stats.stats.host_stats.receive_frame_count;

> +

> +	vport->fc_host_stats.fcp_input_requests =

> +			xport->fcp_stats.input_requests;

> +	vport->fc_host_stats.fcp_output_requests =

> +			xport->fcp_stats.output_requests;

> +	vport->fc_host_stats.fcp_output_megabytes =

> +			xport->fcp_stats.output_bytes >> 20;

> +	vport->fc_host_stats.fcp_input_megabytes =

> +			xport->fcp_stats.input_bytes >> 20;

> +	vport->fc_host_stats.fcp_control_requests =

> +			xport->fcp_stats.control_requests;

> +

> +	return &vport->fc_host_stats;

> +}

> +

> +static void

> +efct_reset_stats(struct Scsi_Host *shost)

> +{

> +	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;

> +	struct efct *efct = vport->efct;

> +	/* argument has no purpose for this action */

> +	union efct_xport_stats_u dummy;

> +	u32 rc = 0;

> +

> +	rc = efct_xport_status(efct->xport, EFCT_XPORT_LINK_STAT_RESET, &dummy);

> +	if (rc != 0)

> +		pr_err("efct_xport_status returned non 0 - %d\n", rc);

> +}

> +

> +static void

> +efct_get_starget_port_id(struct scsi_target *starget)

> +{

> +	pr_err("%s\n", __func__);

> +}

> +

> +static void

> +efct_get_starget_node_name(struct scsi_target *starget)

> +{

> +	pr_err("%s\n", __func__);

> +}

> +

> +static void

> +efct_get_starget_port_name(struct scsi_target *starget)

> +{

> +	pr_err("%s\n", __func__);

> +}

> +

> +static void

> +efct_set_vport_symbolic_name(struct fc_vport *fc_vport)

> +{

> +	pr_err("%s\n", __func__);

> +}


I'm pretty certain you don't need these stubs...

> +

> +static int

> +efct_issue_lip(struct Scsi_Host *shost)

> +{

> +	struct efct_vport *vport =

> +			shost ? (struct efct_vport *)shost->hostdata : NULL;

> +	struct efct *efct = vport ? vport->efct : NULL;

> +

> +	if (!shost || !vport || !efct) {

> +		pr_err("%s: shost=%p vport=%p efct=%p\n", __func__,

> +		       shost, vport, efct);

> +		return -EPERM;

> +	}

> +

> +	/*

> +	 * Bring the link down gracefully then re-init the link.

> +	 * The firmware will re-initialize the Fibre Channel interface as

> +	 * required. It does not issue a LIP.

> +	 */

> +

> +	if (efct_xport_control(efct->xport, EFCT_XPORT_PORT_OFFLINE))

> +		efc_log_debug(efct, "EFCT_XPORT_PORT_OFFLINE failed\n");

> +

> +	if (efct_xport_control(efct->xport, EFCT_XPORT_PORT_ONLINE))

> +		efc_log_debug(efct, "EFCT_XPORT_PORT_ONLINE failed\n");

> +

> +	return EFC_SUCCESS;

> +}

> +

> +struct efct_vport *

> +efct_scsi_new_vport(struct efct *efct, struct device *dev)

> +{

> +	struct Scsi_Host *shost = NULL;

> +	int error = 0;

> +	struct efct_vport *vport = NULL;

> +	union efct_xport_stats_u speed;

> +	u32 supported_speeds = 0;

> +

> +	shost = scsi_host_alloc(&efct_template, sizeof(*vport));

> +	if (!shost) {

> +		efc_log_err(efct, "failed to allocate Scsi_Host struct\n");

> +		return NULL;

> +	}

> +

> +	/* save efct information to shost LLD-specific space */

> +	vport = (struct efct_vport *)shost->hostdata;

> +	vport->efct = efct;

> +	vport->is_vport = true;

> +

> +	shost->can_queue = efct->hw.config.n_io;

> +	shost->max_cmd_len = 16; /* 16-byte CDBs */

> +	shost->max_id = 0xffff;

> +	shost->max_lun = 0xffffffff;

> +

> +	/* can only accept (from mid-layer) as many SGEs as we've pre-regited*/

> +	shost->sg_tablesize = sli_get_max_sgl(&efct->hw.sli);

> +

> +	/* attach FC Transport template to shost */

> +	shost->transportt = efct_vport_fc_tt;

> +	efc_log_debug(efct, "vport transport template=%p\n",

> +		       efct_vport_fc_tt);

> +

> +	/* get pci_dev structure and add host to SCSI ML */

> +	error = scsi_add_host_with_dma(shost, dev, &efct->pci->dev);

> +	if (error) {

> +		efc_log_debug(efct, "failed scsi_add_host_with_dma\n");

> +		return NULL;

> +	}

> +

> +	/* Set symbolic name for host port */

> +	snprintf(fc_host_symbolic_name(shost),

> +		 sizeof(fc_host_symbolic_name(shost)),

> +		 "Emulex %s FV%s DV%s", efct->model, efct->hw.sli.fw_name[0],

> +		 EFCT_DRIVER_VERSION);

> +

> +	/* Set host port supported classes */

> +	fc_host_supported_classes(shost) = FC_COS_CLASS3;

> +

> +	speed.value = 1000;

> +	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,

> +			      &speed)) {

> +		supported_speeds |= FC_PORTSPEED_1GBIT;

> +	}

> +	speed.value = 2000;

> +	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,

> +			      &speed)) {

> +		supported_speeds |= FC_PORTSPEED_2GBIT;

> +	}

> +	speed.value = 4000;

> +	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,

> +			      &speed)) {

> +		supported_speeds |= FC_PORTSPEED_4GBIT;

> +	}

> +	speed.value = 8000;

> +	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,

> +			      &speed)) {

> +		supported_speeds |= FC_PORTSPEED_8GBIT;

> +	}

> +	speed.value = 10000;

> +	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,

> +			      &speed)) {

> +		supported_speeds |= FC_PORTSPEED_10GBIT;

> +	}

> +	speed.value = 16000;

> +	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,

> +			      &speed)) {

> +		supported_speeds |= FC_PORTSPEED_16GBIT;

> +	}

> +	speed.value = 32000;

> +	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,

> +			      &speed)) {

> +		supported_speeds |= FC_PORTSPEED_32GBIT;

> +	}

> +

> +	fc_host_supported_speeds(shost) = supported_speeds;

> +	vport->shost = shost;

> +

> +	return vport;

> +}

> +

> +int efct_scsi_del_vport(struct efct *efct, struct Scsi_Host *shost)

> +{

> +	if (shost) {

> +		efc_log_debug(efct,

> +				"Unregistering vport with Transport Layer\n");

> +		efct_xport_remove_host(shost);

> +		efc_log_debug(efct, "Unregistering vport with SCSI Midlayer\n");

> +		scsi_remove_host(shost);

> +		scsi_host_put(shost);

> +		return EFC_SUCCESS;

> +	}

> +	return EFC_FAIL;

> +}

> +

> +static int

> +efct_vport_create(struct fc_vport *fc_vport, bool disable)

> +{

> +	struct Scsi_Host *shost = fc_vport ? fc_vport->shost : NULL;

> +	struct efct_vport *pport = shost ?

> +					(struct efct_vport *)shost->hostdata :

> +					NULL;

> +	struct efct *efct = pport ? pport->efct : NULL;

> +	struct efct_vport *vport = NULL;

> +

> +	if (!fc_vport || !shost || !efct)

> +		goto fail;

> +

> +	vport = efct_scsi_new_vport(efct, &fc_vport->dev);

> +	if (!vport) {

> +		efc_log_err(efct, "failed to create vport\n");

> +		goto fail;

> +	}

> +

> +	vport->fc_vport = fc_vport;

> +	vport->npiv_wwpn = fc_vport->port_name;

> +	vport->npiv_wwnn = fc_vport->node_name;

> +	fc_host_node_name(vport->shost) = vport->npiv_wwnn;

> +	fc_host_port_name(vport->shost) = vport->npiv_wwpn;

> +	*(struct efct_vport **)fc_vport->dd_data = vport;

> +

> +	return EFC_SUCCESS;

> +

> +fail:

> +	return EFC_FAIL;

> +}

> +

> +static int

> +efct_vport_delete(struct fc_vport *fc_vport)

> +{

> +	struct efct_vport *vport = *(struct efct_vport **)fc_vport->dd_data;

> +	struct Scsi_Host *shost = vport ? vport->shost : NULL;

> +	struct efct *efct = vport ? vport->efct : NULL;

> +	int rc = -1;

> +

> +	rc = efct_scsi_del_vport(efct, shost);

> +

> +	if (rc)

> +		pr_err("%s: vport delete failed\n", __func__);

> +

> +	return rc;

> +}

> +

> +static int

> +efct_vport_disable(struct fc_vport *fc_vport, bool disable)

> +{

> +	return EFC_SUCCESS;

> +}

> +

> +static struct fc_function_template efct_xport_functions = {

> +	.get_starget_node_name = efct_get_starget_node_name,

> +	.get_starget_port_name = efct_get_starget_port_name,

> +	.get_starget_port_id  = efct_get_starget_port_id,

> +

> +	.get_host_port_id = efct_get_host_port_id,

> +	.get_host_port_type = efct_get_host_port_type,

> +	.get_host_port_state = efct_get_host_port_state,

> +	.get_host_speed = efct_get_host_speed,

> +	.get_host_fabric_name = efct_get_host_fabric_name,

> +

> +	.get_fc_host_stats = efct_get_stats,

> +	.reset_fc_host_stats = efct_reset_stats,

> +

> +	.issue_fc_host_lip = efct_issue_lip,

> +

> +	.set_vport_symbolic_name = efct_set_vport_symbolic_name,

> +	.vport_disable = efct_vport_disable,

> +

> +	/* allocation lengths for host-specific data */

> +	.dd_fcrport_size = sizeof(struct efct_rport_data),

> +	.dd_fcvport_size = 128, /* should be sizeof(...) */

> +

> +	/* remote port fixed attributes */

> +	.show_rport_maxframe_size = 1,

> +	.show_rport_supported_classes = 1,

> +	.show_rport_dev_loss_tmo = 1,

> +

> +	/* target dynamic attributes */

> +	.show_starget_node_name = 1,

> +	.show_starget_port_name = 1,

> +	.show_starget_port_id = 1,

> +

> +	/* host fixed attributes */

> +	.show_host_node_name = 1,

> +	.show_host_port_name = 1,

> +	.show_host_supported_classes = 1,

> +	.show_host_supported_fc4s = 1,

> +	.show_host_supported_speeds = 1,

> +	.show_host_maxframe_size = 1,

> +

> +	/* host dynamic attributes */

> +	.show_host_port_id = 1,

> +	.show_host_port_type = 1,

> +	.show_host_port_state = 1,

> +	/* active_fc4s is shown but doesn't change (thus no get function) */

> +	.show_host_active_fc4s = 1,

> +	.show_host_speed = 1,

> +	.show_host_fabric_name = 1,

> +	.show_host_symbolic_name = 1,

> +	.vport_create = efct_vport_create,

> +	.vport_delete = efct_vport_delete,

> +};

> +

> +static struct fc_function_template efct_vport_functions = {

> +	.get_starget_node_name = efct_get_starget_node_name,

> +	.get_starget_port_name = efct_get_starget_port_name,

> +	.get_starget_port_id  = efct_get_starget_port_id,

> +

> +	.get_host_port_id = efct_get_host_port_id,

> +	.get_host_port_type = efct_get_host_vport_type,

> +	.get_host_port_state = efct_get_host_port_state,

> +	.get_host_speed = efct_get_host_speed,

> +	.get_host_fabric_name = efct_get_host_fabric_name,

> +

> +	.get_fc_host_stats = efct_get_stats,

> +	.reset_fc_host_stats = efct_reset_stats,

> +

> +	.issue_fc_host_lip = efct_issue_lip,

> +	.set_vport_symbolic_name = efct_set_vport_symbolic_name,

> +

> +	/* allocation lengths for host-specific data */

> +	.dd_fcrport_size = sizeof(struct efct_rport_data),

> +	.dd_fcvport_size = 128, /* should be sizeof(...) */

> +

> +	/* remote port fixed attributes */

> +	.show_rport_maxframe_size = 1,

> +	.show_rport_supported_classes = 1,

> +	.show_rport_dev_loss_tmo = 1,

> +

> +	/* target dynamic attributes */

> +	.show_starget_node_name = 1,

> +	.show_starget_port_name = 1,

> +	.show_starget_port_id = 1,

> +

> +	/* host fixed attributes */

> +	.show_host_node_name = 1,

> +	.show_host_port_name = 1,

> +	.show_host_supported_classes = 1,

> +	.show_host_supported_fc4s = 1,

> +	.show_host_supported_speeds = 1,

> +	.show_host_maxframe_size = 1,

> +

> +	/* host dynamic attributes */

> +	.show_host_port_id = 1,

> +	.show_host_port_type = 1,

> +	.show_host_port_state = 1,

> +	/* active_fc4s is shown but doesn't change (thus no get function) */

> +	.show_host_active_fc4s = 1,

> +	.show_host_speed = 1,

> +	.show_host_fabric_name = 1,

> +	.show_host_symbolic_name = 1,

> +};

> 

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                Kernel Storage Architect
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
diff mbox series

Patch

diff --git a/drivers/scsi/elx/efct/efct_xport.c b/drivers/scsi/elx/efct/efct_xport.c
index 0cf558c03547..bee13337afbc 100644
--- a/drivers/scsi/elx/efct/efct_xport.c
+++ b/drivers/scsi/elx/efct/efct_xport.c
@@ -778,3 +778,503 @@  int efct_scsi_del_device(struct efct *efct)
 	}
 	return EFC_SUCCESS;
 }
+
+static void
+efct_get_host_port_id(struct Scsi_Host *shost)
+{
+	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+	struct efct *efct = vport->efct;
+	struct efc *efc = efct->efcport;
+	struct efc_nport *nport;
+
+	if (efc->domain && efc->domain->nport) {
+		nport = efc->domain->nport;
+		fc_host_port_id(shost) = nport->fc_id;
+	}
+}
+
+static void
+efct_get_host_port_type(struct Scsi_Host *shost)
+{
+	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+	struct efct *efct = vport->efct;
+	struct efc *efc = efct->efcport;
+	int type = FC_PORTTYPE_UNKNOWN;
+
+	if (efc->domain && efc->domain->nport) {
+		if (efc->domain->is_loop) {
+			type = FC_PORTTYPE_LPORT;
+		} else {
+			struct efc_nport *nport = efc->domain->nport;
+
+			if (nport->is_vport)
+				type = FC_PORTTYPE_NPIV;
+			else if (nport->topology == EFC_NPORT_TOPO_P2P)
+				type = FC_PORTTYPE_PTP;
+			else if (nport->topology == EFC_NPORT_TOPO_UNKNOWN)
+				type = FC_PORTTYPE_UNKNOWN;
+			else
+				type = FC_PORTTYPE_NPORT;
+		}
+	}
+	fc_host_port_type(shost) = type;
+}
+
+static void
+efct_get_host_vport_type(struct Scsi_Host *shost)
+{
+	fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+}
+
+static void
+efct_get_host_port_state(struct Scsi_Host *shost)
+{
+	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+	struct efct *efct = vport->efct;
+	struct efc *efc = efct->efcport;
+
+	if (efc->domain)
+		fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+	else
+		fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+}
+
+static void
+efct_get_host_speed(struct Scsi_Host *shost)
+{
+	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+	struct efct *efct = vport->efct;
+	struct efc *efc = efct->efcport;
+	union efct_xport_stats_u speed;
+	u32 fc_speed = FC_PORTSPEED_UNKNOWN;
+	int rc;
+
+	if (!efc->domain || !efc->domain->nport) {
+		fc_host_speed(shost) = fc_speed;
+		return;
+	}
+
+	rc = efct_xport_status(efct->xport, EFCT_XPORT_LINK_SPEED, &speed);
+	if (rc == 0) {
+		switch (speed.value) {
+		case 1000:
+			fc_speed = FC_PORTSPEED_1GBIT;
+			break;
+		case 2000:
+			fc_speed = FC_PORTSPEED_2GBIT;
+			break;
+		case 4000:
+			fc_speed = FC_PORTSPEED_4GBIT;
+			break;
+		case 8000:
+			fc_speed = FC_PORTSPEED_8GBIT;
+			break;
+		case 10000:
+			fc_speed = FC_PORTSPEED_10GBIT;
+			break;
+		case 16000:
+			fc_speed = FC_PORTSPEED_16GBIT;
+			break;
+		case 32000:
+			fc_speed = FC_PORTSPEED_32GBIT;
+			break;
+		}
+	}
+
+	fc_host_speed(shost) = fc_speed;
+}
+
+static void
+efct_get_host_fabric_name(struct Scsi_Host *shost)
+{
+	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+	struct efct *efct = vport->efct;
+	struct efc *efc = efct->efcport;
+
+	if (efc->domain) {
+		struct fc_els_flogi  *sp =
+			(struct fc_els_flogi  *)
+				efc->domain->flogi_service_params;
+
+		fc_host_fabric_name(shost) = be64_to_cpu(sp->fl_wwnn);
+	}
+}
+
+static struct fc_host_statistics *
+efct_get_stats(struct Scsi_Host *shost)
+{
+	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+	struct efct *efct = vport->efct;
+	union efct_xport_stats_u stats;
+	struct efct_xport *xport = efct->xport;
+	u32 rc = 1;
+
+	rc = efct_xport_status(xport, EFCT_XPORT_LINK_STATISTICS, &stats);
+	if (rc != 0) {
+		pr_err("efct_xport_status returned non 0 - %d\n", rc);
+		return NULL;
+	}
+
+	vport->fc_host_stats.loss_of_sync_count =
+		stats.stats.link_stats.loss_of_sync_error_count;
+	vport->fc_host_stats.link_failure_count =
+		stats.stats.link_stats.link_failure_error_count;
+	vport->fc_host_stats.prim_seq_protocol_err_count =
+		stats.stats.link_stats.primitive_sequence_error_count;
+	vport->fc_host_stats.invalid_tx_word_count =
+		stats.stats.link_stats.invalid_transmission_word_error_count;
+	vport->fc_host_stats.invalid_crc_count =
+		stats.stats.link_stats.crc_error_count;
+	/* mbox returns kbyte count so we need to convert to words */
+	vport->fc_host_stats.tx_words =
+		stats.stats.host_stats.transmit_kbyte_count * 256;
+	/* mbox returns kbyte count so we need to convert to words */
+	vport->fc_host_stats.rx_words =
+		stats.stats.host_stats.receive_kbyte_count * 256;
+	vport->fc_host_stats.tx_frames =
+		stats.stats.host_stats.transmit_frame_count;
+	vport->fc_host_stats.rx_frames =
+		stats.stats.host_stats.receive_frame_count;
+
+	vport->fc_host_stats.fcp_input_requests =
+			xport->fcp_stats.input_requests;
+	vport->fc_host_stats.fcp_output_requests =
+			xport->fcp_stats.output_requests;
+	vport->fc_host_stats.fcp_output_megabytes =
+			xport->fcp_stats.output_bytes >> 20;
+	vport->fc_host_stats.fcp_input_megabytes =
+			xport->fcp_stats.input_bytes >> 20;
+	vport->fc_host_stats.fcp_control_requests =
+			xport->fcp_stats.control_requests;
+
+	return &vport->fc_host_stats;
+}
+
+static void
+efct_reset_stats(struct Scsi_Host *shost)
+{
+	struct efct_vport *vport = (struct efct_vport *)shost->hostdata;
+	struct efct *efct = vport->efct;
+	/* argument has no purpose for this action */
+	union efct_xport_stats_u dummy;
+	u32 rc = 0;
+
+	rc = efct_xport_status(efct->xport, EFCT_XPORT_LINK_STAT_RESET, &dummy);
+	if (rc != 0)
+		pr_err("efct_xport_status returned non 0 - %d\n", rc);
+}
+
+static void
+efct_get_starget_port_id(struct scsi_target *starget)
+{
+	pr_err("%s\n", __func__);
+}
+
+static void
+efct_get_starget_node_name(struct scsi_target *starget)
+{
+	pr_err("%s\n", __func__);
+}
+
+static void
+efct_get_starget_port_name(struct scsi_target *starget)
+{
+	pr_err("%s\n", __func__);
+}
+
+static void
+efct_set_vport_symbolic_name(struct fc_vport *fc_vport)
+{
+	pr_err("%s\n", __func__);
+}
+
+static int
+efct_issue_lip(struct Scsi_Host *shost)
+{
+	struct efct_vport *vport =
+			shost ? (struct efct_vport *)shost->hostdata : NULL;
+	struct efct *efct = vport ? vport->efct : NULL;
+
+	if (!shost || !vport || !efct) {
+		pr_err("%s: shost=%p vport=%p efct=%p\n", __func__,
+		       shost, vport, efct);
+		return -EPERM;
+	}
+
+	/*
+	 * Bring the link down gracefully then re-init the link.
+	 * The firmware will re-initialize the Fibre Channel interface as
+	 * required. It does not issue a LIP.
+	 */
+
+	if (efct_xport_control(efct->xport, EFCT_XPORT_PORT_OFFLINE))
+		efc_log_debug(efct, "EFCT_XPORT_PORT_OFFLINE failed\n");
+
+	if (efct_xport_control(efct->xport, EFCT_XPORT_PORT_ONLINE))
+		efc_log_debug(efct, "EFCT_XPORT_PORT_ONLINE failed\n");
+
+	return EFC_SUCCESS;
+}
+
+struct efct_vport *
+efct_scsi_new_vport(struct efct *efct, struct device *dev)
+{
+	struct Scsi_Host *shost = NULL;
+	int error = 0;
+	struct efct_vport *vport = NULL;
+	union efct_xport_stats_u speed;
+	u32 supported_speeds = 0;
+
+	shost = scsi_host_alloc(&efct_template, sizeof(*vport));
+	if (!shost) {
+		efc_log_err(efct, "failed to allocate Scsi_Host struct\n");
+		return NULL;
+	}
+
+	/* save efct information to shost LLD-specific space */
+	vport = (struct efct_vport *)shost->hostdata;
+	vport->efct = efct;
+	vport->is_vport = true;
+
+	shost->can_queue = efct->hw.config.n_io;
+	shost->max_cmd_len = 16; /* 16-byte CDBs */
+	shost->max_id = 0xffff;
+	shost->max_lun = 0xffffffff;
+
+	/* can only accept (from mid-layer) as many SGEs as we've pre-regited*/
+	shost->sg_tablesize = sli_get_max_sgl(&efct->hw.sli);
+
+	/* attach FC Transport template to shost */
+	shost->transportt = efct_vport_fc_tt;
+	efc_log_debug(efct, "vport transport template=%p\n",
+		       efct_vport_fc_tt);
+
+	/* get pci_dev structure and add host to SCSI ML */
+	error = scsi_add_host_with_dma(shost, dev, &efct->pci->dev);
+	if (error) {
+		efc_log_debug(efct, "failed scsi_add_host_with_dma\n");
+		return NULL;
+	}
+
+	/* Set symbolic name for host port */
+	snprintf(fc_host_symbolic_name(shost),
+		 sizeof(fc_host_symbolic_name(shost)),
+		 "Emulex %s FV%s DV%s", efct->model, efct->hw.sli.fw_name[0],
+		 EFCT_DRIVER_VERSION);
+
+	/* Set host port supported classes */
+	fc_host_supported_classes(shost) = FC_COS_CLASS3;
+
+	speed.value = 1000;
+	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
+			      &speed)) {
+		supported_speeds |= FC_PORTSPEED_1GBIT;
+	}
+	speed.value = 2000;
+	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
+			      &speed)) {
+		supported_speeds |= FC_PORTSPEED_2GBIT;
+	}
+	speed.value = 4000;
+	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
+			      &speed)) {
+		supported_speeds |= FC_PORTSPEED_4GBIT;
+	}
+	speed.value = 8000;
+	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
+			      &speed)) {
+		supported_speeds |= FC_PORTSPEED_8GBIT;
+	}
+	speed.value = 10000;
+	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
+			      &speed)) {
+		supported_speeds |= FC_PORTSPEED_10GBIT;
+	}
+	speed.value = 16000;
+	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
+			      &speed)) {
+		supported_speeds |= FC_PORTSPEED_16GBIT;
+	}
+	speed.value = 32000;
+	if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED,
+			      &speed)) {
+		supported_speeds |= FC_PORTSPEED_32GBIT;
+	}
+
+	fc_host_supported_speeds(shost) = supported_speeds;
+	vport->shost = shost;
+
+	return vport;
+}
+
+int efct_scsi_del_vport(struct efct *efct, struct Scsi_Host *shost)
+{
+	if (shost) {
+		efc_log_debug(efct,
+				"Unregistering vport with Transport Layer\n");
+		efct_xport_remove_host(shost);
+		efc_log_debug(efct, "Unregistering vport with SCSI Midlayer\n");
+		scsi_remove_host(shost);
+		scsi_host_put(shost);
+		return EFC_SUCCESS;
+	}
+	return EFC_FAIL;
+}
+
+static int
+efct_vport_create(struct fc_vport *fc_vport, bool disable)
+{
+	struct Scsi_Host *shost = fc_vport ? fc_vport->shost : NULL;
+	struct efct_vport *pport = shost ?
+					(struct efct_vport *)shost->hostdata :
+					NULL;
+	struct efct *efct = pport ? pport->efct : NULL;
+	struct efct_vport *vport = NULL;
+
+	if (!fc_vport || !shost || !efct)
+		goto fail;
+
+	vport = efct_scsi_new_vport(efct, &fc_vport->dev);
+	if (!vport) {
+		efc_log_err(efct, "failed to create vport\n");
+		goto fail;
+	}
+
+	vport->fc_vport = fc_vport;
+	vport->npiv_wwpn = fc_vport->port_name;
+	vport->npiv_wwnn = fc_vport->node_name;
+	fc_host_node_name(vport->shost) = vport->npiv_wwnn;
+	fc_host_port_name(vport->shost) = vport->npiv_wwpn;
+	*(struct efct_vport **)fc_vport->dd_data = vport;
+
+	return EFC_SUCCESS;
+
+fail:
+	return EFC_FAIL;
+}
+
+static int
+efct_vport_delete(struct fc_vport *fc_vport)
+{
+	struct efct_vport *vport = *(struct efct_vport **)fc_vport->dd_data;
+	struct Scsi_Host *shost = vport ? vport->shost : NULL;
+	struct efct *efct = vport ? vport->efct : NULL;
+	int rc = -1;
+
+	rc = efct_scsi_del_vport(efct, shost);
+
+	if (rc)
+		pr_err("%s: vport delete failed\n", __func__);
+
+	return rc;
+}
+
+static int
+efct_vport_disable(struct fc_vport *fc_vport, bool disable)
+{
+	return EFC_SUCCESS;
+}
+
+static struct fc_function_template efct_xport_functions = {
+	.get_starget_node_name = efct_get_starget_node_name,
+	.get_starget_port_name = efct_get_starget_port_name,
+	.get_starget_port_id  = efct_get_starget_port_id,
+
+	.get_host_port_id = efct_get_host_port_id,
+	.get_host_port_type = efct_get_host_port_type,
+	.get_host_port_state = efct_get_host_port_state,
+	.get_host_speed = efct_get_host_speed,
+	.get_host_fabric_name = efct_get_host_fabric_name,
+
+	.get_fc_host_stats = efct_get_stats,
+	.reset_fc_host_stats = efct_reset_stats,
+
+	.issue_fc_host_lip = efct_issue_lip,
+
+	.set_vport_symbolic_name = efct_set_vport_symbolic_name,
+	.vport_disable = efct_vport_disable,
+
+	/* allocation lengths for host-specific data */
+	.dd_fcrport_size = sizeof(struct efct_rport_data),
+	.dd_fcvport_size = 128, /* should be sizeof(...) */
+
+	/* remote port fixed attributes */
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+	.show_rport_dev_loss_tmo = 1,
+
+	/* target dynamic attributes */
+	.show_starget_node_name = 1,
+	.show_starget_port_name = 1,
+	.show_starget_port_id = 1,
+
+	/* host fixed attributes */
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_supported_fc4s = 1,
+	.show_host_supported_speeds = 1,
+	.show_host_maxframe_size = 1,
+
+	/* host dynamic attributes */
+	.show_host_port_id = 1,
+	.show_host_port_type = 1,
+	.show_host_port_state = 1,
+	/* active_fc4s is shown but doesn't change (thus no get function) */
+	.show_host_active_fc4s = 1,
+	.show_host_speed = 1,
+	.show_host_fabric_name = 1,
+	.show_host_symbolic_name = 1,
+	.vport_create = efct_vport_create,
+	.vport_delete = efct_vport_delete,
+};
+
+static struct fc_function_template efct_vport_functions = {
+	.get_starget_node_name = efct_get_starget_node_name,
+	.get_starget_port_name = efct_get_starget_port_name,
+	.get_starget_port_id  = efct_get_starget_port_id,
+
+	.get_host_port_id = efct_get_host_port_id,
+	.get_host_port_type = efct_get_host_vport_type,
+	.get_host_port_state = efct_get_host_port_state,
+	.get_host_speed = efct_get_host_speed,
+	.get_host_fabric_name = efct_get_host_fabric_name,
+
+	.get_fc_host_stats = efct_get_stats,
+	.reset_fc_host_stats = efct_reset_stats,
+
+	.issue_fc_host_lip = efct_issue_lip,
+	.set_vport_symbolic_name = efct_set_vport_symbolic_name,
+
+	/* allocation lengths for host-specific data */
+	.dd_fcrport_size = sizeof(struct efct_rport_data),
+	.dd_fcvport_size = 128, /* should be sizeof(...) */
+
+	/* remote port fixed attributes */
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+	.show_rport_dev_loss_tmo = 1,
+
+	/* target dynamic attributes */
+	.show_starget_node_name = 1,
+	.show_starget_port_name = 1,
+	.show_starget_port_id = 1,
+
+	/* host fixed attributes */
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_supported_fc4s = 1,
+	.show_host_supported_speeds = 1,
+	.show_host_maxframe_size = 1,
+
+	/* host dynamic attributes */
+	.show_host_port_id = 1,
+	.show_host_port_type = 1,
+	.show_host_port_state = 1,
+	/* active_fc4s is shown but doesn't change (thus no get function) */
+	.show_host_active_fc4s = 1,
+	.show_host_speed = 1,
+	.show_host_fabric_name = 1,
+	.show_host_symbolic_name = 1,
+};