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