162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
462306a36Sopenharmony_ci * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * NPORT
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Port object for physical port and NPIV ports.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * NPORT REFERENCE COUNTING
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * A nport reference should be taken when:
1762306a36Sopenharmony_ci * - an nport is allocated
1862306a36Sopenharmony_ci * - a vport populates associated nport
1962306a36Sopenharmony_ci * - a remote node is allocated
2062306a36Sopenharmony_ci * - a unsolicited frame is processed
2162306a36Sopenharmony_ci * The reference should be dropped when:
2262306a36Sopenharmony_ci * - the unsolicited frame processesing is done
2362306a36Sopenharmony_ci * - the remote node is removed
2462306a36Sopenharmony_ci * - the vport is removed
2562306a36Sopenharmony_ci * - the nport is removed
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "efc.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_civoid
3162306a36Sopenharmony_ciefc_nport_cb(void *arg, int event, void *data)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct efc *efc = arg;
3462306a36Sopenharmony_ci	struct efc_nport *nport = data;
3562306a36Sopenharmony_ci	unsigned long flags = 0;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	efc_log_debug(efc, "nport event: %s\n", efc_sm_event_name(event));
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
4062306a36Sopenharmony_ci	efc_sm_post_event(&nport->sm, event, NULL);
4162306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic struct efc_nport *
4562306a36Sopenharmony_ciefc_nport_find_wwn(struct efc_domain *domain, uint64_t wwnn, uint64_t wwpn)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct efc_nport *nport = NULL;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* Find a nport, given the WWNN and WWPN */
5062306a36Sopenharmony_ci	list_for_each_entry(nport, &domain->nport_list, list_entry) {
5162306a36Sopenharmony_ci		if (nport->wwnn == wwnn && nport->wwpn == wwpn)
5262306a36Sopenharmony_ci			return nport;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	return NULL;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void
5862306a36Sopenharmony_ci_efc_nport_free(struct kref *arg)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct efc_nport *nport = container_of(arg, struct efc_nport, ref);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	kfree(nport);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct efc_nport *
6662306a36Sopenharmony_ciefc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
6762306a36Sopenharmony_ci		u32 fc_id, bool enable_ini, bool enable_tgt)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct efc_nport *nport;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (domain->efc->enable_ini)
7262306a36Sopenharmony_ci		enable_ini = 0;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Return a failure if this nport has already been allocated */
7562306a36Sopenharmony_ci	if ((wwpn != 0) || (wwnn != 0)) {
7662306a36Sopenharmony_ci		nport = efc_nport_find_wwn(domain, wwnn, wwpn);
7762306a36Sopenharmony_ci		if (nport) {
7862306a36Sopenharmony_ci			efc_log_err(domain->efc,
7962306a36Sopenharmony_ci				    "NPORT %016llX %016llX already allocated\n",
8062306a36Sopenharmony_ci				    wwnn, wwpn);
8162306a36Sopenharmony_ci			return NULL;
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	nport = kzalloc(sizeof(*nport), GFP_ATOMIC);
8662306a36Sopenharmony_ci	if (!nport)
8762306a36Sopenharmony_ci		return nport;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* initialize refcount */
9062306a36Sopenharmony_ci	kref_init(&nport->ref);
9162306a36Sopenharmony_ci	nport->release = _efc_nport_free;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	nport->efc = domain->efc;
9462306a36Sopenharmony_ci	snprintf(nport->display_name, sizeof(nport->display_name), "------");
9562306a36Sopenharmony_ci	nport->domain = domain;
9662306a36Sopenharmony_ci	xa_init(&nport->lookup);
9762306a36Sopenharmony_ci	nport->instance_index = domain->nport_count++;
9862306a36Sopenharmony_ci	nport->sm.app = nport;
9962306a36Sopenharmony_ci	nport->enable_ini = enable_ini;
10062306a36Sopenharmony_ci	nport->enable_tgt = enable_tgt;
10162306a36Sopenharmony_ci	nport->enable_rscn = (nport->enable_ini ||
10262306a36Sopenharmony_ci			(nport->enable_tgt && enable_target_rscn(nport->efc)));
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Copy service parameters from domain */
10562306a36Sopenharmony_ci	memcpy(nport->service_params, domain->service_params,
10662306a36Sopenharmony_ci	       sizeof(struct fc_els_flogi));
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Update requested fc_id */
10962306a36Sopenharmony_ci	nport->fc_id = fc_id;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Update the nport's service parameters for the new wwn's */
11262306a36Sopenharmony_ci	nport->wwpn = wwpn;
11362306a36Sopenharmony_ci	nport->wwnn = wwnn;
11462306a36Sopenharmony_ci	snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), "%016llX",
11562306a36Sopenharmony_ci		 (unsigned long long)wwnn);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/*
11862306a36Sopenharmony_ci	 * if this is the "first" nport of the domain,
11962306a36Sopenharmony_ci	 * then make it the "phys" nport
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	if (list_empty(&domain->nport_list))
12262306a36Sopenharmony_ci		domain->nport = nport;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	INIT_LIST_HEAD(&nport->list_entry);
12562306a36Sopenharmony_ci	list_add_tail(&nport->list_entry, &domain->nport_list);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	kref_get(&domain->ref);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	efc_log_debug(domain->efc, "New Nport [%s]\n", nport->display_name);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return nport;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_civoid
13562306a36Sopenharmony_ciefc_nport_free(struct efc_nport *nport)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct efc_domain *domain;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (!nport)
14062306a36Sopenharmony_ci		return;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	domain = nport->domain;
14362306a36Sopenharmony_ci	efc_log_debug(domain->efc, "[%s] free nport\n", nport->display_name);
14462306a36Sopenharmony_ci	list_del(&nport->list_entry);
14562306a36Sopenharmony_ci	/*
14662306a36Sopenharmony_ci	 * if this is the physical nport,
14762306a36Sopenharmony_ci	 * then clear it out of the domain
14862306a36Sopenharmony_ci	 */
14962306a36Sopenharmony_ci	if (nport == domain->nport)
15062306a36Sopenharmony_ci		domain->nport = NULL;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	xa_destroy(&nport->lookup);
15362306a36Sopenharmony_ci	xa_erase(&domain->lookup, nport->fc_id);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (list_empty(&domain->nport_list))
15662306a36Sopenharmony_ci		efc_domain_post_event(domain, EFC_EVT_ALL_CHILD_NODES_FREE,
15762306a36Sopenharmony_ci				      NULL);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	kref_put(&domain->ref, domain->release);
16062306a36Sopenharmony_ci	kref_put(&nport->ref, nport->release);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistruct efc_nport *
16462306a36Sopenharmony_ciefc_nport_find(struct efc_domain *domain, u32 d_id)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct efc_nport *nport;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* Find a nport object, given an FC_ID */
16962306a36Sopenharmony_ci	nport = xa_load(&domain->lookup, d_id);
17062306a36Sopenharmony_ci	if (!nport || !kref_get_unless_zero(&nport->ref))
17162306a36Sopenharmony_ci		return NULL;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return nport;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ciint
17762306a36Sopenharmony_ciefc_nport_attach(struct efc_nport *nport, u32 fc_id)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int rc;
18062306a36Sopenharmony_ci	struct efc_node *node;
18162306a36Sopenharmony_ci	struct efc *efc = nport->efc;
18262306a36Sopenharmony_ci	unsigned long index;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* Set our lookup */
18562306a36Sopenharmony_ci	rc = xa_err(xa_store(&nport->domain->lookup, fc_id, nport, GFP_ATOMIC));
18662306a36Sopenharmony_ci	if (rc) {
18762306a36Sopenharmony_ci		efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
18862306a36Sopenharmony_ci		return rc;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Update our display_name */
19262306a36Sopenharmony_ci	efc_node_fcid_display(fc_id, nport->display_name,
19362306a36Sopenharmony_ci			      sizeof(nport->display_name));
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	xa_for_each(&nport->lookup, index, node) {
19662306a36Sopenharmony_ci		efc_node_update_display_name(node);
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	efc_log_debug(nport->efc, "[%s] attach nport: fc_id x%06x\n",
20062306a36Sopenharmony_ci		      nport->display_name, fc_id);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Register a nport, given an FC_ID */
20362306a36Sopenharmony_ci	rc = efc_cmd_nport_attach(efc, nport, fc_id);
20462306a36Sopenharmony_ci	if (rc < 0) {
20562306a36Sopenharmony_ci		efc_log_err(nport->efc,
20662306a36Sopenharmony_ci			    "efc_hw_port_attach failed: %d\n", rc);
20762306a36Sopenharmony_ci		return -EIO;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void
21362306a36Sopenharmony_ciefc_nport_shutdown(struct efc_nport *nport)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct efc *efc = nport->efc;
21662306a36Sopenharmony_ci	struct efc_node *node;
21762306a36Sopenharmony_ci	unsigned long index;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	xa_for_each(&nport->lookup, index, node) {
22062306a36Sopenharmony_ci		if (!(node->rnode.fc_id == FC_FID_FLOGI && nport->is_vport)) {
22162306a36Sopenharmony_ci			efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
22262306a36Sopenharmony_ci			continue;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		/*
22662306a36Sopenharmony_ci		 * If this is a vport, logout of the fabric
22762306a36Sopenharmony_ci		 * controller so that it deletes the vport
22862306a36Sopenharmony_ci		 * on the switch.
22962306a36Sopenharmony_ci		 */
23062306a36Sopenharmony_ci		/* if link is down, don't send logo */
23162306a36Sopenharmony_ci		if (efc->link_status == EFC_LINK_STATUS_DOWN) {
23262306a36Sopenharmony_ci			efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL);
23362306a36Sopenharmony_ci			continue;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		efc_log_debug(efc, "[%s] nport shutdown vport, send logo\n",
23762306a36Sopenharmony_ci			      node->display_name);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		if (!efc_send_logo(node)) {
24062306a36Sopenharmony_ci			/* sent LOGO, wait for response */
24162306a36Sopenharmony_ci			efc_node_transition(node, __efc_d_wait_logo_rsp, NULL);
24262306a36Sopenharmony_ci			continue;
24362306a36Sopenharmony_ci		}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		/*
24662306a36Sopenharmony_ci		 * failed to send LOGO,
24762306a36Sopenharmony_ci		 * go ahead and cleanup node anyways
24862306a36Sopenharmony_ci		 */
24962306a36Sopenharmony_ci		node_printf(node, "Failed to send LOGO\n");
25062306a36Sopenharmony_ci		efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic void
25562306a36Sopenharmony_ciefc_vport_link_down(struct efc_nport *nport)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct efc *efc = nport->efc;
25862306a36Sopenharmony_ci	struct efc_vport *vport;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Clear the nport reference in the vport specification */
26162306a36Sopenharmony_ci	list_for_each_entry(vport, &efc->vport_list, list_entry) {
26262306a36Sopenharmony_ci		if (vport->nport == nport) {
26362306a36Sopenharmony_ci			kref_put(&nport->ref, nport->release);
26462306a36Sopenharmony_ci			vport->nport = NULL;
26562306a36Sopenharmony_ci			break;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic void
27162306a36Sopenharmony_ci__efc_nport_common(const char *funcname, struct efc_sm_ctx *ctx,
27262306a36Sopenharmony_ci		   enum efc_sm_event evt, void *arg)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct efc_nport *nport = ctx->app;
27562306a36Sopenharmony_ci	struct efc_domain *domain = nport->domain;
27662306a36Sopenharmony_ci	struct efc *efc = nport->efc;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	switch (evt) {
27962306a36Sopenharmony_ci	case EFC_EVT_ENTER:
28062306a36Sopenharmony_ci	case EFC_EVT_REENTER:
28162306a36Sopenharmony_ci	case EFC_EVT_EXIT:
28262306a36Sopenharmony_ci	case EFC_EVT_ALL_CHILD_NODES_FREE:
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	case EFC_EVT_NPORT_ATTACH_OK:
28562306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_nport_attached, NULL);
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	case EFC_EVT_SHUTDOWN:
28862306a36Sopenharmony_ci		/* Flag this nport as shutting down */
28962306a36Sopenharmony_ci		nport->shutting_down = true;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		if (nport->is_vport)
29262306a36Sopenharmony_ci			efc_vport_link_down(nport);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (xa_empty(&nport->lookup)) {
29562306a36Sopenharmony_ci			/* Remove the nport from the domain's lookup table */
29662306a36Sopenharmony_ci			xa_erase(&domain->lookup, nport->fc_id);
29762306a36Sopenharmony_ci			efc_sm_transition(ctx, __efc_nport_wait_port_free,
29862306a36Sopenharmony_ci					  NULL);
29962306a36Sopenharmony_ci			if (efc_cmd_nport_free(efc, nport)) {
30062306a36Sopenharmony_ci				efc_log_debug(nport->efc,
30162306a36Sopenharmony_ci					      "efc_hw_port_free failed\n");
30262306a36Sopenharmony_ci				/* Not much we can do, free the nport anyways */
30362306a36Sopenharmony_ci				efc_nport_free(nport);
30462306a36Sopenharmony_ci			}
30562306a36Sopenharmony_ci		} else {
30662306a36Sopenharmony_ci			/* sm: node list is not empty / shutdown nodes */
30762306a36Sopenharmony_ci			efc_sm_transition(ctx,
30862306a36Sopenharmony_ci					  __efc_nport_wait_shutdown, NULL);
30962306a36Sopenharmony_ci			efc_nport_shutdown(nport);
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci		break;
31262306a36Sopenharmony_ci	default:
31362306a36Sopenharmony_ci		efc_log_debug(nport->efc, "[%s] %-20s %-20s not handled\n",
31462306a36Sopenharmony_ci			      nport->display_name, funcname,
31562306a36Sopenharmony_ci			      efc_sm_event_name(evt));
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_civoid
32062306a36Sopenharmony_ci__efc_nport_allocated(struct efc_sm_ctx *ctx,
32162306a36Sopenharmony_ci		      enum efc_sm_event evt, void *arg)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct efc_nport *nport = ctx->app;
32462306a36Sopenharmony_ci	struct efc_domain *domain = nport->domain;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	nport_sm_trace(nport);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	switch (evt) {
32962306a36Sopenharmony_ci	/* the physical nport is attached */
33062306a36Sopenharmony_ci	case EFC_EVT_NPORT_ATTACH_OK:
33162306a36Sopenharmony_ci		WARN_ON(nport != domain->nport);
33262306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_nport_attached, NULL);
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	case EFC_EVT_NPORT_ALLOC_OK:
33662306a36Sopenharmony_ci		/* ignore */
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	default:
33962306a36Sopenharmony_ci		__efc_nport_common(__func__, ctx, evt, arg);
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_civoid
34462306a36Sopenharmony_ci__efc_nport_vport_init(struct efc_sm_ctx *ctx,
34562306a36Sopenharmony_ci		       enum efc_sm_event evt, void *arg)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct efc_nport *nport = ctx->app;
34862306a36Sopenharmony_ci	struct efc *efc = nport->efc;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	nport_sm_trace(nport);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	switch (evt) {
35362306a36Sopenharmony_ci	case EFC_EVT_ENTER: {
35462306a36Sopenharmony_ci		__be64 be_wwpn = cpu_to_be64(nport->wwpn);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		if (nport->wwpn == 0)
35762306a36Sopenharmony_ci			efc_log_debug(efc, "vport: letting f/w select WWN\n");
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		if (nport->fc_id != U32_MAX) {
36062306a36Sopenharmony_ci			efc_log_debug(efc, "vport: hard coding port id: %x\n",
36162306a36Sopenharmony_ci				      nport->fc_id);
36262306a36Sopenharmony_ci		}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_nport_vport_wait_alloc, NULL);
36562306a36Sopenharmony_ci		/* If wwpn is zero, then we'll let the f/w assign wwpn*/
36662306a36Sopenharmony_ci		if (efc_cmd_nport_alloc(efc, nport, nport->domain,
36762306a36Sopenharmony_ci					nport->wwpn == 0 ? NULL :
36862306a36Sopenharmony_ci					(uint8_t *)&be_wwpn)) {
36962306a36Sopenharmony_ci			efc_log_err(efc, "Can't allocate port\n");
37062306a36Sopenharmony_ci			break;
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		break;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci	default:
37662306a36Sopenharmony_ci		__efc_nport_common(__func__, ctx, evt, arg);
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_civoid
38162306a36Sopenharmony_ci__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx,
38262306a36Sopenharmony_ci			     enum efc_sm_event evt, void *arg)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct efc_nport *nport = ctx->app;
38562306a36Sopenharmony_ci	struct efc *efc = nport->efc;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	nport_sm_trace(nport);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	switch (evt) {
39062306a36Sopenharmony_ci	case EFC_EVT_NPORT_ALLOC_OK: {
39162306a36Sopenharmony_ci		struct fc_els_flogi *sp;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		sp = (struct fc_els_flogi *)nport->service_params;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		if (nport->wwnn == 0) {
39662306a36Sopenharmony_ci			nport->wwnn = be64_to_cpu(nport->sli_wwnn);
39762306a36Sopenharmony_ci			nport->wwpn = be64_to_cpu(nport->sli_wwpn);
39862306a36Sopenharmony_ci			snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),
39962306a36Sopenharmony_ci				 "%016llX", nport->wwpn);
40062306a36Sopenharmony_ci		}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		/* Update the nport's service parameters */
40362306a36Sopenharmony_ci		sp->fl_wwpn = cpu_to_be64(nport->wwpn);
40462306a36Sopenharmony_ci		sp->fl_wwnn = cpu_to_be64(nport->wwnn);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		/*
40762306a36Sopenharmony_ci		 * if nport->fc_id is uninitialized,
40862306a36Sopenharmony_ci		 * then request that the fabric node use FDISC
40962306a36Sopenharmony_ci		 * to find an fc_id.
41062306a36Sopenharmony_ci		 * Otherwise we're restoring vports, or we're in
41162306a36Sopenharmony_ci		 * fabric emulation mode, so attach the fc_id
41262306a36Sopenharmony_ci		 */
41362306a36Sopenharmony_ci		if (nport->fc_id == U32_MAX) {
41462306a36Sopenharmony_ci			struct efc_node *fabric;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci			fabric = efc_node_alloc(nport, FC_FID_FLOGI, false,
41762306a36Sopenharmony_ci						false);
41862306a36Sopenharmony_ci			if (!fabric) {
41962306a36Sopenharmony_ci				efc_log_err(efc, "efc_node_alloc() failed\n");
42062306a36Sopenharmony_ci				return;
42162306a36Sopenharmony_ci			}
42262306a36Sopenharmony_ci			efc_node_transition(fabric, __efc_vport_fabric_init,
42362306a36Sopenharmony_ci					    NULL);
42462306a36Sopenharmony_ci		} else {
42562306a36Sopenharmony_ci			snprintf(nport->wwnn_str, sizeof(nport->wwnn_str),
42662306a36Sopenharmony_ci				 "%016llX", nport->wwpn);
42762306a36Sopenharmony_ci			efc_nport_attach(nport, nport->fc_id);
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_nport_vport_allocated, NULL);
43062306a36Sopenharmony_ci		break;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci	default:
43362306a36Sopenharmony_ci		__efc_nport_common(__func__, ctx, evt, arg);
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_civoid
43862306a36Sopenharmony_ci__efc_nport_vport_allocated(struct efc_sm_ctx *ctx,
43962306a36Sopenharmony_ci			    enum efc_sm_event evt, void *arg)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct efc_nport *nport = ctx->app;
44262306a36Sopenharmony_ci	struct efc *efc = nport->efc;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	nport_sm_trace(nport);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/*
44762306a36Sopenharmony_ci	 * This state is entered after the nport is allocated;
44862306a36Sopenharmony_ci	 * it then waits for a fabric node
44962306a36Sopenharmony_ci	 * FDISC to complete, which requests a nport attach.
45062306a36Sopenharmony_ci	 * The nport attach complete is handled in this state.
45162306a36Sopenharmony_ci	 */
45262306a36Sopenharmony_ci	switch (evt) {
45362306a36Sopenharmony_ci	case EFC_EVT_NPORT_ATTACH_OK: {
45462306a36Sopenharmony_ci		struct efc_node *node;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		/* Find our fabric node, and forward this event */
45762306a36Sopenharmony_ci		node = efc_node_find(nport, FC_FID_FLOGI);
45862306a36Sopenharmony_ci		if (!node) {
45962306a36Sopenharmony_ci			efc_log_debug(efc, "can't find node %06x\n", FC_FID_FLOGI);
46062306a36Sopenharmony_ci			break;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci		/* sm: / forward nport attach to fabric node */
46362306a36Sopenharmony_ci		efc_node_post_event(node, evt, NULL);
46462306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_nport_attached, NULL);
46562306a36Sopenharmony_ci		break;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci	default:
46862306a36Sopenharmony_ci		__efc_nport_common(__func__, ctx, evt, arg);
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void
47362306a36Sopenharmony_ciefc_vport_update_spec(struct efc_nport *nport)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct efc *efc = nport->efc;
47662306a36Sopenharmony_ci	struct efc_vport *vport;
47762306a36Sopenharmony_ci	unsigned long flags = 0;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	spin_lock_irqsave(&efc->vport_lock, flags);
48062306a36Sopenharmony_ci	list_for_each_entry(vport, &efc->vport_list, list_entry) {
48162306a36Sopenharmony_ci		if (vport->nport == nport) {
48262306a36Sopenharmony_ci			vport->wwnn = nport->wwnn;
48362306a36Sopenharmony_ci			vport->wwpn = nport->wwpn;
48462306a36Sopenharmony_ci			vport->tgt_data = nport->tgt_data;
48562306a36Sopenharmony_ci			vport->ini_data = nport->ini_data;
48662306a36Sopenharmony_ci			break;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->vport_lock, flags);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_civoid
49362306a36Sopenharmony_ci__efc_nport_attached(struct efc_sm_ctx *ctx,
49462306a36Sopenharmony_ci		     enum efc_sm_event evt, void *arg)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct efc_nport *nport = ctx->app;
49762306a36Sopenharmony_ci	struct efc *efc = nport->efc;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	nport_sm_trace(nport);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	switch (evt) {
50262306a36Sopenharmony_ci	case EFC_EVT_ENTER: {
50362306a36Sopenharmony_ci		struct efc_node *node;
50462306a36Sopenharmony_ci		unsigned long index;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		efc_log_debug(efc,
50762306a36Sopenharmony_ci			      "[%s] NPORT attached WWPN %016llX WWNN %016llX\n",
50862306a36Sopenharmony_ci			      nport->display_name,
50962306a36Sopenharmony_ci			      nport->wwpn, nport->wwnn);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		xa_for_each(&nport->lookup, index, node)
51262306a36Sopenharmony_ci			efc_node_update_display_name(node);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		efc->tt.new_nport(efc, nport);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		/*
51762306a36Sopenharmony_ci		 * Update the vport (if its not the physical nport)
51862306a36Sopenharmony_ci		 * parameters
51962306a36Sopenharmony_ci		 */
52062306a36Sopenharmony_ci		if (nport->is_vport)
52162306a36Sopenharmony_ci			efc_vport_update_spec(nport);
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	case EFC_EVT_EXIT:
52662306a36Sopenharmony_ci		efc_log_debug(efc,
52762306a36Sopenharmony_ci			      "[%s] NPORT deattached WWPN %016llX WWNN %016llX\n",
52862306a36Sopenharmony_ci			      nport->display_name,
52962306a36Sopenharmony_ci			      nport->wwpn, nport->wwnn);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		efc->tt.del_nport(efc, nport);
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	default:
53462306a36Sopenharmony_ci		__efc_nport_common(__func__, ctx, evt, arg);
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_civoid
53962306a36Sopenharmony_ci__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx,
54062306a36Sopenharmony_ci			  enum efc_sm_event evt, void *arg)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct efc_nport *nport = ctx->app;
54362306a36Sopenharmony_ci	struct efc_domain *domain = nport->domain;
54462306a36Sopenharmony_ci	struct efc *efc = nport->efc;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	nport_sm_trace(nport);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	switch (evt) {
54962306a36Sopenharmony_ci	case EFC_EVT_NPORT_ALLOC_OK:
55062306a36Sopenharmony_ci	case EFC_EVT_NPORT_ALLOC_FAIL:
55162306a36Sopenharmony_ci	case EFC_EVT_NPORT_ATTACH_OK:
55262306a36Sopenharmony_ci	case EFC_EVT_NPORT_ATTACH_FAIL:
55362306a36Sopenharmony_ci		/* ignore these events - just wait for the all free event */
55462306a36Sopenharmony_ci		break;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	case EFC_EVT_ALL_CHILD_NODES_FREE: {
55762306a36Sopenharmony_ci		/*
55862306a36Sopenharmony_ci		 * Remove the nport from the domain's
55962306a36Sopenharmony_ci		 * sparse vector lookup table
56062306a36Sopenharmony_ci		 */
56162306a36Sopenharmony_ci		xa_erase(&domain->lookup, nport->fc_id);
56262306a36Sopenharmony_ci		efc_sm_transition(ctx, __efc_nport_wait_port_free, NULL);
56362306a36Sopenharmony_ci		if (efc_cmd_nport_free(efc, nport)) {
56462306a36Sopenharmony_ci			efc_log_err(nport->efc, "efc_hw_port_free failed\n");
56562306a36Sopenharmony_ci			/* Not much we can do, free the nport anyways */
56662306a36Sopenharmony_ci			efc_nport_free(nport);
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci		break;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci	default:
57162306a36Sopenharmony_ci		__efc_nport_common(__func__, ctx, evt, arg);
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_civoid
57662306a36Sopenharmony_ci__efc_nport_wait_port_free(struct efc_sm_ctx *ctx,
57762306a36Sopenharmony_ci			   enum efc_sm_event evt, void *arg)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct efc_nport *nport = ctx->app;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	nport_sm_trace(nport);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	switch (evt) {
58462306a36Sopenharmony_ci	case EFC_EVT_NPORT_ATTACH_OK:
58562306a36Sopenharmony_ci		/* Ignore as we are waiting for the free CB */
58662306a36Sopenharmony_ci		break;
58762306a36Sopenharmony_ci	case EFC_EVT_NPORT_FREE_OK: {
58862306a36Sopenharmony_ci		/* All done, free myself */
58962306a36Sopenharmony_ci		efc_nport_free(nport);
59062306a36Sopenharmony_ci		break;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci	default:
59362306a36Sopenharmony_ci		__efc_nport_common(__func__, ctx, evt, arg);
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic int
59862306a36Sopenharmony_ciefc_vport_nport_alloc(struct efc_domain *domain, struct efc_vport *vport)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct efc_nport *nport;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	lockdep_assert_held(&domain->efc->lock);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	nport = efc_nport_alloc(domain, vport->wwpn, vport->wwnn, vport->fc_id,
60562306a36Sopenharmony_ci				vport->enable_ini, vport->enable_tgt);
60662306a36Sopenharmony_ci	vport->nport = nport;
60762306a36Sopenharmony_ci	if (!nport)
60862306a36Sopenharmony_ci		return -EIO;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	kref_get(&nport->ref);
61162306a36Sopenharmony_ci	nport->is_vport = true;
61262306a36Sopenharmony_ci	nport->tgt_data = vport->tgt_data;
61362306a36Sopenharmony_ci	nport->ini_data = vport->ini_data;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	efc_sm_transition(&nport->sm, __efc_nport_vport_init, NULL);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return 0;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ciint
62162306a36Sopenharmony_ciefc_vport_start(struct efc_domain *domain)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	struct efc *efc = domain->efc;
62462306a36Sopenharmony_ci	struct efc_vport *vport;
62562306a36Sopenharmony_ci	struct efc_vport *next;
62662306a36Sopenharmony_ci	int rc = 0;
62762306a36Sopenharmony_ci	unsigned long flags = 0;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Use the vport spec to find the associated vports and start them */
63062306a36Sopenharmony_ci	spin_lock_irqsave(&efc->vport_lock, flags);
63162306a36Sopenharmony_ci	list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
63262306a36Sopenharmony_ci		if (!vport->nport) {
63362306a36Sopenharmony_ci			if (efc_vport_nport_alloc(domain, vport))
63462306a36Sopenharmony_ci				rc = -EIO;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->vport_lock, flags);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return rc;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ciint
64362306a36Sopenharmony_ciefc_nport_vport_new(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn,
64462306a36Sopenharmony_ci		    u32 fc_id, bool ini, bool tgt, void *tgt_data,
64562306a36Sopenharmony_ci		    void *ini_data)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct efc *efc = domain->efc;
64862306a36Sopenharmony_ci	struct efc_vport *vport;
64962306a36Sopenharmony_ci	int rc = 0;
65062306a36Sopenharmony_ci	unsigned long flags = 0;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (ini && domain->efc->enable_ini == 0) {
65362306a36Sopenharmony_ci		efc_log_debug(efc, "driver initiator mode not enabled\n");
65462306a36Sopenharmony_ci		return -EIO;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (tgt && domain->efc->enable_tgt == 0) {
65862306a36Sopenharmony_ci		efc_log_debug(efc, "driver target mode not enabled\n");
65962306a36Sopenharmony_ci		return -EIO;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/*
66362306a36Sopenharmony_ci	 * Create a vport spec if we need to recreate
66462306a36Sopenharmony_ci	 * this vport after a link up event
66562306a36Sopenharmony_ci	 */
66662306a36Sopenharmony_ci	vport = efc_vport_create_spec(domain->efc, wwnn, wwpn, fc_id, ini, tgt,
66762306a36Sopenharmony_ci				      tgt_data, ini_data);
66862306a36Sopenharmony_ci	if (!vport) {
66962306a36Sopenharmony_ci		efc_log_err(efc, "failed to create vport object entry\n");
67062306a36Sopenharmony_ci		return -EIO;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
67462306a36Sopenharmony_ci	rc = efc_vport_nport_alloc(domain, vport);
67562306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return rc;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ciint
68162306a36Sopenharmony_ciefc_nport_vport_del(struct efc *efc, struct efc_domain *domain,
68262306a36Sopenharmony_ci		    u64 wwpn, uint64_t wwnn)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	struct efc_nport *nport;
68562306a36Sopenharmony_ci	struct efc_vport *vport;
68662306a36Sopenharmony_ci	struct efc_vport *next;
68762306a36Sopenharmony_ci	unsigned long flags = 0;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	spin_lock_irqsave(&efc->vport_lock, flags);
69062306a36Sopenharmony_ci	/* walk the efc_vport_list and remove from there */
69162306a36Sopenharmony_ci	list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
69262306a36Sopenharmony_ci		if (vport->wwpn == wwpn && vport->wwnn == wwnn) {
69362306a36Sopenharmony_ci			list_del(&vport->list_entry);
69462306a36Sopenharmony_ci			kfree(vport);
69562306a36Sopenharmony_ci			break;
69662306a36Sopenharmony_ci		}
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->vport_lock, flags);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (!domain) {
70162306a36Sopenharmony_ci		/* No domain means no nport to look for */
70262306a36Sopenharmony_ci		return 0;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	spin_lock_irqsave(&efc->lock, flags);
70662306a36Sopenharmony_ci	list_for_each_entry(nport, &domain->nport_list, list_entry) {
70762306a36Sopenharmony_ci		if (nport->wwpn == wwpn && nport->wwnn == wwnn) {
70862306a36Sopenharmony_ci			kref_put(&nport->ref, nport->release);
70962306a36Sopenharmony_ci			/* Shutdown this NPORT */
71062306a36Sopenharmony_ci			efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL);
71162306a36Sopenharmony_ci			break;
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->lock, flags);
71662306a36Sopenharmony_ci	return 0;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_civoid
72062306a36Sopenharmony_ciefc_vport_del_all(struct efc *efc)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct efc_vport *vport;
72362306a36Sopenharmony_ci	struct efc_vport *next;
72462306a36Sopenharmony_ci	unsigned long flags = 0;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	spin_lock_irqsave(&efc->vport_lock, flags);
72762306a36Sopenharmony_ci	list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) {
72862306a36Sopenharmony_ci		list_del(&vport->list_entry);
72962306a36Sopenharmony_ci		kfree(vport);
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->vport_lock, flags);
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistruct efc_vport *
73562306a36Sopenharmony_ciefc_vport_create_spec(struct efc *efc, uint64_t wwnn, uint64_t wwpn,
73662306a36Sopenharmony_ci		      u32 fc_id, bool enable_ini,
73762306a36Sopenharmony_ci		      bool enable_tgt, void *tgt_data, void *ini_data)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct efc_vport *vport;
74062306a36Sopenharmony_ci	unsigned long flags = 0;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	/*
74362306a36Sopenharmony_ci	 * walk the efc_vport_list and return failure
74462306a36Sopenharmony_ci	 * if a valid(vport with non zero WWPN and WWNN) vport entry
74562306a36Sopenharmony_ci	 * is already created
74662306a36Sopenharmony_ci	 */
74762306a36Sopenharmony_ci	spin_lock_irqsave(&efc->vport_lock, flags);
74862306a36Sopenharmony_ci	list_for_each_entry(vport, &efc->vport_list, list_entry) {
74962306a36Sopenharmony_ci		if ((wwpn && vport->wwpn == wwpn) &&
75062306a36Sopenharmony_ci		    (wwnn && vport->wwnn == wwnn)) {
75162306a36Sopenharmony_ci			efc_log_err(efc,
75262306a36Sopenharmony_ci				    "VPORT %016llX %016llX already allocated\n",
75362306a36Sopenharmony_ci				    wwnn, wwpn);
75462306a36Sopenharmony_ci			spin_unlock_irqrestore(&efc->vport_lock, flags);
75562306a36Sopenharmony_ci			return NULL;
75662306a36Sopenharmony_ci		}
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	vport = kzalloc(sizeof(*vport), GFP_ATOMIC);
76062306a36Sopenharmony_ci	if (!vport) {
76162306a36Sopenharmony_ci		spin_unlock_irqrestore(&efc->vport_lock, flags);
76262306a36Sopenharmony_ci		return NULL;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	vport->wwnn = wwnn;
76662306a36Sopenharmony_ci	vport->wwpn = wwpn;
76762306a36Sopenharmony_ci	vport->fc_id = fc_id;
76862306a36Sopenharmony_ci	vport->enable_tgt = enable_tgt;
76962306a36Sopenharmony_ci	vport->enable_ini = enable_ini;
77062306a36Sopenharmony_ci	vport->tgt_data = tgt_data;
77162306a36Sopenharmony_ci	vport->ini_data = ini_data;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	INIT_LIST_HEAD(&vport->list_entry);
77462306a36Sopenharmony_ci	list_add_tail(&vport->list_entry, &efc->vport_list);
77562306a36Sopenharmony_ci	spin_unlock_irqrestore(&efc->vport_lock, flags);
77662306a36Sopenharmony_ci	return vport;
77762306a36Sopenharmony_ci}
778