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 * domain_sm Domain State Machine: States 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "efc.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciint 1462306a36Sopenharmony_ciefc_domain_cb(void *arg, int event, void *data) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct efc *efc = arg; 1762306a36Sopenharmony_ci struct efc_domain *domain = NULL; 1862306a36Sopenharmony_ci int rc = 0; 1962306a36Sopenharmony_ci unsigned long flags = 0; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci if (event != EFC_HW_DOMAIN_FOUND) 2262306a36Sopenharmony_ci domain = data; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* Accept domain callback events from the user driver */ 2562306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 2662306a36Sopenharmony_ci switch (event) { 2762306a36Sopenharmony_ci case EFC_HW_DOMAIN_FOUND: { 2862306a36Sopenharmony_ci u64 fcf_wwn = 0; 2962306a36Sopenharmony_ci struct efc_domain_record *drec = data; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* extract the fcf_wwn */ 3262306a36Sopenharmony_ci fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn)); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* lookup domain, or allocate a new one */ 3762306a36Sopenharmony_ci domain = efc->domain; 3862306a36Sopenharmony_ci if (!domain) { 3962306a36Sopenharmony_ci domain = efc_domain_alloc(efc, fcf_wwn); 4062306a36Sopenharmony_ci if (!domain) { 4162306a36Sopenharmony_ci efc_log_err(efc, "efc_domain_alloc() failed\n"); 4262306a36Sopenharmony_ci rc = -1; 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci efc_sm_transition(&domain->drvsm, __efc_domain_init, 4662306a36Sopenharmony_ci NULL); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec); 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci case EFC_HW_DOMAIN_LOST: 5362306a36Sopenharmony_ci domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n"); 5462306a36Sopenharmony_ci efc->hold_frames = true; 5562306a36Sopenharmony_ci efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL); 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci case EFC_HW_DOMAIN_ALLOC_OK: 5962306a36Sopenharmony_ci domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n"); 6062306a36Sopenharmony_ci efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL); 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci case EFC_HW_DOMAIN_ALLOC_FAIL: 6462306a36Sopenharmony_ci domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n"); 6562306a36Sopenharmony_ci efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL, 6662306a36Sopenharmony_ci NULL); 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci case EFC_HW_DOMAIN_ATTACH_OK: 7062306a36Sopenharmony_ci domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n"); 7162306a36Sopenharmony_ci efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL); 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci case EFC_HW_DOMAIN_ATTACH_FAIL: 7562306a36Sopenharmony_ci domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n"); 7662306a36Sopenharmony_ci efc_domain_post_event(domain, 7762306a36Sopenharmony_ci EFC_EVT_DOMAIN_ATTACH_FAIL, NULL); 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci case EFC_HW_DOMAIN_FREE_OK: 8162306a36Sopenharmony_ci domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n"); 8262306a36Sopenharmony_ci efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL); 8362306a36Sopenharmony_ci break; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci case EFC_HW_DOMAIN_FREE_FAIL: 8662306a36Sopenharmony_ci domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n"); 8762306a36Sopenharmony_ci efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL); 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci default: 9162306a36Sopenharmony_ci efc_log_warn(efc, "unsupported event %#x\n", event); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (efc->domain && domain->req_accept_frames) { 9662306a36Sopenharmony_ci domain->req_accept_frames = false; 9762306a36Sopenharmony_ci efc->hold_frames = false; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return rc; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void 10462306a36Sopenharmony_ci_efc_domain_free(struct kref *arg) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct efc_domain *domain = container_of(arg, struct efc_domain, ref); 10762306a36Sopenharmony_ci struct efc *efc = domain->efc; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (efc->domain_free_cb) 11062306a36Sopenharmony_ci (*efc->domain_free_cb)(efc, efc->domain_free_cb_arg); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci kfree(domain); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid 11662306a36Sopenharmony_ciefc_domain_free(struct efc_domain *domain) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct efc *efc; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci efc = domain->efc; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Hold frames to clear the domain pointer from the xport lookup */ 12362306a36Sopenharmony_ci efc->hold_frames = false; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci xa_destroy(&domain->lookup); 12862306a36Sopenharmony_ci efc->domain = NULL; 12962306a36Sopenharmony_ci kref_put(&domain->ref, domain->release); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct efc_domain * 13362306a36Sopenharmony_ciefc_domain_alloc(struct efc *efc, uint64_t fcf_wwn) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct efc_domain *domain; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci domain = kzalloc(sizeof(*domain), GFP_ATOMIC); 13862306a36Sopenharmony_ci if (!domain) 13962306a36Sopenharmony_ci return NULL; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci domain->efc = efc; 14262306a36Sopenharmony_ci domain->drvsm.app = domain; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* initialize refcount */ 14562306a36Sopenharmony_ci kref_init(&domain->ref); 14662306a36Sopenharmony_ci domain->release = _efc_domain_free; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci xa_init(&domain->lookup); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci INIT_LIST_HEAD(&domain->nport_list); 15162306a36Sopenharmony_ci efc->domain = domain; 15262306a36Sopenharmony_ci domain->fcf_wwn = fcf_wwn; 15362306a36Sopenharmony_ci efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return domain; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_civoid 15962306a36Sopenharmony_ciefc_register_domain_free_cb(struct efc *efc, 16062306a36Sopenharmony_ci void (*callback)(struct efc *efc, void *arg), 16162306a36Sopenharmony_ci void *arg) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci /* Register a callback to be called when the domain is freed */ 16462306a36Sopenharmony_ci efc->domain_free_cb = callback; 16562306a36Sopenharmony_ci efc->domain_free_cb_arg = arg; 16662306a36Sopenharmony_ci if (!efc->domain && callback) 16762306a36Sopenharmony_ci (*callback)(efc, arg); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void 17162306a36Sopenharmony_ci__efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx, 17262306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct efc_domain *domain = ctx->app; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci switch (evt) { 17762306a36Sopenharmony_ci case EFC_EVT_ENTER: 17862306a36Sopenharmony_ci case EFC_EVT_REENTER: 17962306a36Sopenharmony_ci case EFC_EVT_EXIT: 18062306a36Sopenharmony_ci case EFC_EVT_ALL_CHILD_NODES_FREE: 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * this can arise if an FLOGI fails on the NPORT, 18362306a36Sopenharmony_ci * and the NPORT is shutdown 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci default: 18762306a36Sopenharmony_ci efc_log_warn(domain->efc, "%-20s %-20s not handled\n", 18862306a36Sopenharmony_ci funcname, efc_sm_event_name(evt)); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void 19362306a36Sopenharmony_ci__efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx, 19462306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct efc_domain *domain = ctx->app; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci switch (evt) { 19962306a36Sopenharmony_ci case EFC_EVT_ENTER: 20062306a36Sopenharmony_ci case EFC_EVT_REENTER: 20162306a36Sopenharmony_ci case EFC_EVT_EXIT: 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci case EFC_EVT_DOMAIN_FOUND: 20462306a36Sopenharmony_ci /* save drec, mark domain_found_pending */ 20562306a36Sopenharmony_ci memcpy(&domain->pending_drec, arg, 20662306a36Sopenharmony_ci sizeof(domain->pending_drec)); 20762306a36Sopenharmony_ci domain->domain_found_pending = true; 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci case EFC_EVT_DOMAIN_LOST: 21062306a36Sopenharmony_ci /* unmark domain_found_pending */ 21162306a36Sopenharmony_ci domain->domain_found_pending = false; 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci default: 21562306a36Sopenharmony_ci efc_log_warn(domain->efc, "%-20s %-20s not handled\n", 21662306a36Sopenharmony_ci funcname, efc_sm_event_name(evt)); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci#define std_domain_state_decl(...)\ 22162306a36Sopenharmony_ci struct efc_domain *domain = NULL;\ 22262306a36Sopenharmony_ci struct efc *efc = NULL;\ 22362306a36Sopenharmony_ci \ 22462306a36Sopenharmony_ci WARN_ON(!ctx || !ctx->app);\ 22562306a36Sopenharmony_ci domain = ctx->app;\ 22662306a36Sopenharmony_ci WARN_ON(!domain->efc);\ 22762306a36Sopenharmony_ci efc = domain->efc 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_civoid 23062306a36Sopenharmony_ci__efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, 23162306a36Sopenharmony_ci void *arg) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci std_domain_state_decl(); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci domain_sm_trace(domain); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci switch (evt) { 23862306a36Sopenharmony_ci case EFC_EVT_ENTER: 23962306a36Sopenharmony_ci domain->attached = false; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci case EFC_EVT_DOMAIN_FOUND: { 24362306a36Sopenharmony_ci u32 i; 24462306a36Sopenharmony_ci struct efc_domain_record *drec = arg; 24562306a36Sopenharmony_ci struct efc_nport *nport; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci u64 my_wwnn = efc->req_wwnn; 24862306a36Sopenharmony_ci u64 my_wwpn = efc->req_wwpn; 24962306a36Sopenharmony_ci __be64 bewwpn; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (my_wwpn == 0 || my_wwnn == 0) { 25262306a36Sopenharmony_ci efc_log_debug(efc, "using default hardware WWN config\n"); 25362306a36Sopenharmony_ci my_wwpn = efc->def_wwpn; 25462306a36Sopenharmony_ci my_wwnn = efc->def_wwnn; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n", 25862306a36Sopenharmony_ci my_wwpn, my_wwnn); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Allocate a nport and transition to __efc_nport_allocated */ 26162306a36Sopenharmony_ci nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX, 26262306a36Sopenharmony_ci efc->enable_ini, efc->enable_tgt); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!nport) { 26562306a36Sopenharmony_ci efc_log_err(efc, "efc_nport_alloc() failed\n"); 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci bewwpn = cpu_to_be64(nport->wwpn); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* allocate struct efc_nport object for local port 27362306a36Sopenharmony_ci * Note: drec->fc_id is ALPA from read_topology only if loop 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) { 27662306a36Sopenharmony_ci efc_log_err(efc, "Can't allocate port\n"); 27762306a36Sopenharmony_ci efc_nport_free(nport); 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci domain->is_loop = drec->is_loop; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* 28462306a36Sopenharmony_ci * If the loop position map includes ALPA == 0, 28562306a36Sopenharmony_ci * then we are in a public loop (NL_PORT) 28662306a36Sopenharmony_ci * Note that the first element of the loopmap[] 28762306a36Sopenharmony_ci * contains the count of elements, and if 28862306a36Sopenharmony_ci * ALPA == 0 is present, it will occupy the first 28962306a36Sopenharmony_ci * location after the count. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci domain->is_nlport = drec->map.loop[1] == 0x00; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!domain->is_loop) { 29462306a36Sopenharmony_ci /* Initiate HW domain alloc */ 29562306a36Sopenharmony_ci if (efc_cmd_domain_alloc(efc, domain, drec->index)) { 29662306a36Sopenharmony_ci efc_log_err(efc, 29762306a36Sopenharmony_ci "Failed to initiate HW domain allocation\n"); 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_alloc, arg); 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci efc_log_debug(efc, "%s fc_id=%#x speed=%d\n", 30562306a36Sopenharmony_ci drec->is_loop ? 30662306a36Sopenharmony_ci (domain->is_nlport ? 30762306a36Sopenharmony_ci "public-loop" : "loop") : "other", 30862306a36Sopenharmony_ci drec->fc_id, drec->speed); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci nport->fc_id = drec->fc_id; 31162306a36Sopenharmony_ci nport->topology = EFC_NPORT_TOPO_FC_AL; 31262306a36Sopenharmony_ci snprintf(nport->display_name, sizeof(nport->display_name), 31362306a36Sopenharmony_ci "s%06x", drec->fc_id); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (efc->enable_ini) { 31662306a36Sopenharmony_ci u32 count = drec->map.loop[0]; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci efc_log_debug(efc, "%d position map entries\n", 31962306a36Sopenharmony_ci count); 32062306a36Sopenharmony_ci for (i = 1; i <= count; i++) { 32162306a36Sopenharmony_ci if (drec->map.loop[i] != drec->fc_id) { 32262306a36Sopenharmony_ci struct efc_node *node; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci efc_log_debug(efc, "%#x -> %#x\n", 32562306a36Sopenharmony_ci drec->fc_id, 32662306a36Sopenharmony_ci drec->map.loop[i]); 32762306a36Sopenharmony_ci node = efc_node_alloc(nport, 32862306a36Sopenharmony_ci drec->map.loop[i], 32962306a36Sopenharmony_ci false, true); 33062306a36Sopenharmony_ci if (!node) { 33162306a36Sopenharmony_ci efc_log_err(efc, 33262306a36Sopenharmony_ci "efc_node_alloc() failed\n"); 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci efc_node_transition(node, 33662306a36Sopenharmony_ci __efc_d_wait_loop, 33762306a36Sopenharmony_ci NULL); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Initiate HW domain alloc */ 34362306a36Sopenharmony_ci if (efc_cmd_domain_alloc(efc, domain, drec->index)) { 34462306a36Sopenharmony_ci efc_log_err(efc, 34562306a36Sopenharmony_ci "Failed to initiate HW domain allocation\n"); 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_alloc, arg); 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci default: 35262306a36Sopenharmony_ci __efc_domain_common(__func__, ctx, evt, arg); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_civoid 35762306a36Sopenharmony_ci__efc_domain_wait_alloc(struct efc_sm_ctx *ctx, 35862306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci std_domain_state_decl(); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci domain_sm_trace(domain); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci switch (evt) { 36562306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ALLOC_OK: { 36662306a36Sopenharmony_ci struct fc_els_flogi *sp; 36762306a36Sopenharmony_ci struct efc_nport *nport; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci nport = domain->nport; 37062306a36Sopenharmony_ci if (WARN_ON(!nport)) 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci sp = (struct fc_els_flogi *)nport->service_params; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Save the domain service parameters */ 37662306a36Sopenharmony_ci memcpy(domain->service_params + 4, domain->dma.virt, 37762306a36Sopenharmony_ci sizeof(struct fc_els_flogi) - 4); 37862306a36Sopenharmony_ci memcpy(nport->service_params + 4, domain->dma.virt, 37962306a36Sopenharmony_ci sizeof(struct fc_els_flogi) - 4); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * Update the nport's service parameters, 38362306a36Sopenharmony_ci * user might have specified non-default names 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci sp->fl_wwpn = cpu_to_be64(nport->wwpn); 38662306a36Sopenharmony_ci sp->fl_wwnn = cpu_to_be64(nport->wwnn); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * Take the loop topology path, 39062306a36Sopenharmony_ci * unless we are an NL_PORT (public loop) 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci if (domain->is_loop && !domain->is_nlport) { 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * For loop, we already have our FC ID 39562306a36Sopenharmony_ci * and don't need fabric login. 39662306a36Sopenharmony_ci * Transition to the allocated state and 39762306a36Sopenharmony_ci * post an event to attach to 39862306a36Sopenharmony_ci * the domain. Note that this breaks the 39962306a36Sopenharmony_ci * normal action/transition 40062306a36Sopenharmony_ci * pattern here to avoid a race with the 40162306a36Sopenharmony_ci * domain attach callback. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci /* sm: is_loop / domain_attach */ 40462306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_allocated, NULL); 40562306a36Sopenharmony_ci __efc_domain_attach_internal(domain, nport->fc_id); 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci { 40962306a36Sopenharmony_ci struct efc_node *node; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* alloc fabric node, send FLOGI */ 41262306a36Sopenharmony_ci node = efc_node_find(nport, FC_FID_FLOGI); 41362306a36Sopenharmony_ci if (node) { 41462306a36Sopenharmony_ci efc_log_err(efc, 41562306a36Sopenharmony_ci "Fabric Controller node already exists\n"); 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci node = efc_node_alloc(nport, FC_FID_FLOGI, 41962306a36Sopenharmony_ci false, false); 42062306a36Sopenharmony_ci if (!node) { 42162306a36Sopenharmony_ci efc_log_err(efc, 42262306a36Sopenharmony_ci "Error: efc_node_alloc() failed\n"); 42362306a36Sopenharmony_ci } else { 42462306a36Sopenharmony_ci efc_node_transition(node, 42562306a36Sopenharmony_ci __efc_fabric_init, NULL); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci /* Accept frames */ 42862306a36Sopenharmony_ci domain->req_accept_frames = true; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci /* sm: / start fabric logins */ 43162306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_allocated, NULL); 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ALLOC_FAIL: 43662306a36Sopenharmony_ci efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;", 43762306a36Sopenharmony_ci efc_sm_event_name(evt)); 43862306a36Sopenharmony_ci efc_log_err(efc, "shutting down domain\n"); 43962306a36Sopenharmony_ci domain->req_domain_free = true; 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci case EFC_EVT_DOMAIN_FOUND: 44362306a36Sopenharmony_ci /* Should not happen */ 44462306a36Sopenharmony_ci break; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci case EFC_EVT_DOMAIN_LOST: 44762306a36Sopenharmony_ci efc_log_debug(efc, 44862306a36Sopenharmony_ci "%s received while waiting for hw_domain_alloc()\n", 44962306a36Sopenharmony_ci efc_sm_event_name(evt)); 45062306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL); 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci default: 45462306a36Sopenharmony_ci __efc_domain_common(__func__, ctx, evt, arg); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_civoid 45962306a36Sopenharmony_ci__efc_domain_allocated(struct efc_sm_ctx *ctx, 46062306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci std_domain_state_decl(); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci domain_sm_trace(domain); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci switch (evt) { 46762306a36Sopenharmony_ci case EFC_EVT_DOMAIN_REQ_ATTACH: { 46862306a36Sopenharmony_ci int rc = 0; 46962306a36Sopenharmony_ci u32 fc_id; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (WARN_ON(!arg)) 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci fc_id = *((u32 *)arg); 47562306a36Sopenharmony_ci efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n", 47662306a36Sopenharmony_ci fc_id); 47762306a36Sopenharmony_ci /* Update nport lookup */ 47862306a36Sopenharmony_ci rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport, 47962306a36Sopenharmony_ci GFP_ATOMIC)); 48062306a36Sopenharmony_ci if (rc) { 48162306a36Sopenharmony_ci efc_log_err(efc, "Sport lookup store failed: %d\n", rc); 48262306a36Sopenharmony_ci return; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Update display name for the nport */ 48662306a36Sopenharmony_ci efc_node_fcid_display(fc_id, domain->nport->display_name, 48762306a36Sopenharmony_ci sizeof(domain->nport->display_name)); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Issue domain attach call */ 49062306a36Sopenharmony_ci rc = efc_cmd_domain_attach(efc, domain, fc_id); 49162306a36Sopenharmony_ci if (rc) { 49262306a36Sopenharmony_ci efc_log_err(efc, "efc_hw_domain_attach failed: %d\n", 49362306a36Sopenharmony_ci rc); 49462306a36Sopenharmony_ci return; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci /* sm: / domain_attach */ 49762306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_attach, NULL); 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci case EFC_EVT_DOMAIN_FOUND: 50262306a36Sopenharmony_ci /* Should not happen */ 50362306a36Sopenharmony_ci efc_log_err(efc, "%s: evt: %d should not happen\n", 50462306a36Sopenharmony_ci __func__, evt); 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci case EFC_EVT_DOMAIN_LOST: { 50862306a36Sopenharmony_ci efc_log_debug(efc, 50962306a36Sopenharmony_ci "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n", 51062306a36Sopenharmony_ci efc_sm_event_name(evt)); 51162306a36Sopenharmony_ci if (!list_empty(&domain->nport_list)) { 51262306a36Sopenharmony_ci /* 51362306a36Sopenharmony_ci * if there are nports, transition to 51462306a36Sopenharmony_ci * wait state and send shutdown to each 51562306a36Sopenharmony_ci * nport 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci struct efc_nport *nport = NULL, *nport_next = NULL; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_nports_free, 52062306a36Sopenharmony_ci NULL); 52162306a36Sopenharmony_ci list_for_each_entry_safe(nport, nport_next, 52262306a36Sopenharmony_ci &domain->nport_list, 52362306a36Sopenharmony_ci list_entry) { 52462306a36Sopenharmony_ci efc_sm_post_event(&nport->sm, 52562306a36Sopenharmony_ci EFC_EVT_SHUTDOWN, NULL); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci } else { 52862306a36Sopenharmony_ci /* no nports exist, free domain */ 52962306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_shutdown, 53062306a36Sopenharmony_ci NULL); 53162306a36Sopenharmony_ci if (efc_cmd_domain_free(efc, domain)) 53262306a36Sopenharmony_ci efc_log_err(efc, "hw_domain_free failed\n"); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci default: 53962306a36Sopenharmony_ci __efc_domain_common(__func__, ctx, evt, arg); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_civoid 54462306a36Sopenharmony_ci__efc_domain_wait_attach(struct efc_sm_ctx *ctx, 54562306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci std_domain_state_decl(); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci domain_sm_trace(domain); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci switch (evt) { 55262306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ATTACH_OK: { 55362306a36Sopenharmony_ci struct efc_node *node = NULL; 55462306a36Sopenharmony_ci struct efc_nport *nport, *next_nport; 55562306a36Sopenharmony_ci unsigned long index; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* 55862306a36Sopenharmony_ci * Set domain notify pending state to avoid 55962306a36Sopenharmony_ci * duplicate domain event post 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ci domain->domain_notify_pend = true; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* Mark as attached */ 56462306a36Sopenharmony_ci domain->attached = true; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* Transition to ready */ 56762306a36Sopenharmony_ci /* sm: / forward event to all nports and nodes */ 56862306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_ready, NULL); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* We have an FCFI, so we can accept frames */ 57162306a36Sopenharmony_ci domain->req_accept_frames = true; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* 57462306a36Sopenharmony_ci * Notify all nodes that the domain attach request 57562306a36Sopenharmony_ci * has completed 57662306a36Sopenharmony_ci * Note: nport will have already received notification 57762306a36Sopenharmony_ci * of nport attached as a result of the HW's port attach. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci list_for_each_entry_safe(nport, next_nport, 58062306a36Sopenharmony_ci &domain->nport_list, list_entry) { 58162306a36Sopenharmony_ci xa_for_each(&nport->lookup, index, node) { 58262306a36Sopenharmony_ci efc_node_post_event(node, 58362306a36Sopenharmony_ci EFC_EVT_DOMAIN_ATTACH_OK, 58462306a36Sopenharmony_ci NULL); 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci domain->domain_notify_pend = false; 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ATTACH_FAIL: 59262306a36Sopenharmony_ci efc_log_debug(efc, 59362306a36Sopenharmony_ci "%s received while waiting for hw attach\n", 59462306a36Sopenharmony_ci efc_sm_event_name(evt)); 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci case EFC_EVT_DOMAIN_FOUND: 59862306a36Sopenharmony_ci /* Should not happen */ 59962306a36Sopenharmony_ci efc_log_err(efc, "%s: evt: %d should not happen\n", 60062306a36Sopenharmony_ci __func__, evt); 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci case EFC_EVT_DOMAIN_LOST: 60462306a36Sopenharmony_ci /* 60562306a36Sopenharmony_ci * Domain lost while waiting for an attach to complete, 60662306a36Sopenharmony_ci * go to a state that waits for the domain attach to 60762306a36Sopenharmony_ci * complete, then handle domain lost 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci case EFC_EVT_DOMAIN_REQ_ATTACH: 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * In P2P we can get an attach request from 61562306a36Sopenharmony_ci * the other FLOGI path, so drop this one 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci default: 62062306a36Sopenharmony_ci __efc_domain_common(__func__, ctx, evt, arg); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_civoid 62562306a36Sopenharmony_ci__efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci std_domain_state_decl(); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci domain_sm_trace(domain); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci switch (evt) { 63262306a36Sopenharmony_ci case EFC_EVT_ENTER: { 63362306a36Sopenharmony_ci /* start any pending vports */ 63462306a36Sopenharmony_ci if (efc_vport_start(domain)) { 63562306a36Sopenharmony_ci efc_log_debug(domain->efc, 63662306a36Sopenharmony_ci "efc_vport_start didn't start vports\n"); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci case EFC_EVT_DOMAIN_LOST: { 64162306a36Sopenharmony_ci if (!list_empty(&domain->nport_list)) { 64262306a36Sopenharmony_ci /* 64362306a36Sopenharmony_ci * if there are nports, transition to wait state 64462306a36Sopenharmony_ci * and send shutdown to each nport 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ci struct efc_nport *nport = NULL, *nport_next = NULL; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_nports_free, 64962306a36Sopenharmony_ci NULL); 65062306a36Sopenharmony_ci list_for_each_entry_safe(nport, nport_next, 65162306a36Sopenharmony_ci &domain->nport_list, 65262306a36Sopenharmony_ci list_entry) { 65362306a36Sopenharmony_ci efc_sm_post_event(&nport->sm, 65462306a36Sopenharmony_ci EFC_EVT_SHUTDOWN, NULL); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci } else { 65762306a36Sopenharmony_ci /* no nports exist, free domain */ 65862306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_shutdown, 65962306a36Sopenharmony_ci NULL); 66062306a36Sopenharmony_ci if (efc_cmd_domain_free(efc, domain)) 66162306a36Sopenharmony_ci efc_log_err(efc, "hw_domain_free failed\n"); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci case EFC_EVT_DOMAIN_FOUND: 66762306a36Sopenharmony_ci /* Should not happen */ 66862306a36Sopenharmony_ci efc_log_err(efc, "%s: evt: %d should not happen\n", 66962306a36Sopenharmony_ci __func__, evt); 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci case EFC_EVT_DOMAIN_REQ_ATTACH: { 67362306a36Sopenharmony_ci /* can happen during p2p */ 67462306a36Sopenharmony_ci u32 fc_id; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci fc_id = *((u32 *)arg); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Assume that the domain is attached */ 67962306a36Sopenharmony_ci WARN_ON(!domain->attached); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* 68262306a36Sopenharmony_ci * Verify that the requested FC_ID 68362306a36Sopenharmony_ci * is the same as the one we're working with 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ci WARN_ON(domain->nport->fc_id != fc_id); 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci default: 69062306a36Sopenharmony_ci __efc_domain_common(__func__, ctx, evt, arg); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_civoid 69562306a36Sopenharmony_ci__efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt, 69662306a36Sopenharmony_ci void *arg) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci std_domain_state_decl(); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci domain_sm_trace(domain); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Wait for nodes to free prior to the domain shutdown */ 70362306a36Sopenharmony_ci switch (evt) { 70462306a36Sopenharmony_ci case EFC_EVT_ALL_CHILD_NODES_FREE: { 70562306a36Sopenharmony_ci int rc; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* sm: / efc_hw_domain_free */ 70862306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Request efc_hw_domain_free and wait for completion */ 71162306a36Sopenharmony_ci rc = efc_cmd_domain_free(efc, domain); 71262306a36Sopenharmony_ci if (rc) { 71362306a36Sopenharmony_ci efc_log_err(efc, "efc_hw_domain_free() failed: %d\n", 71462306a36Sopenharmony_ci rc); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci break; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci default: 71962306a36Sopenharmony_ci __efc_domain_common_shutdown(__func__, ctx, evt, arg); 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_civoid 72462306a36Sopenharmony_ci__efc_domain_wait_shutdown(struct efc_sm_ctx *ctx, 72562306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci std_domain_state_decl(); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci domain_sm_trace(domain); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci switch (evt) { 73262306a36Sopenharmony_ci case EFC_EVT_DOMAIN_FREE_OK: 73362306a36Sopenharmony_ci /* sm: / domain_free */ 73462306a36Sopenharmony_ci if (domain->domain_found_pending) { 73562306a36Sopenharmony_ci /* 73662306a36Sopenharmony_ci * save fcf_wwn and drec from this domain, 73762306a36Sopenharmony_ci * free current domain and allocate 73862306a36Sopenharmony_ci * a new one with the same fcf_wwn 73962306a36Sopenharmony_ci * could use a SLI-4 "re-register VPI" 74062306a36Sopenharmony_ci * operation here? 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci u64 fcf_wwn = domain->fcf_wwn; 74362306a36Sopenharmony_ci struct efc_domain_record drec = domain->pending_drec; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci efc_log_debug(efc, "Reallocating domain\n"); 74662306a36Sopenharmony_ci domain->req_domain_free = true; 74762306a36Sopenharmony_ci domain = efc_domain_alloc(efc, fcf_wwn); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (!domain) { 75062306a36Sopenharmony_ci efc_log_err(efc, 75162306a36Sopenharmony_ci "efc_domain_alloc() failed\n"); 75262306a36Sopenharmony_ci return; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci /* 75562306a36Sopenharmony_ci * got a new domain; at this point, 75662306a36Sopenharmony_ci * there are at least two domains 75762306a36Sopenharmony_ci * once the req_domain_free flag is processed, 75862306a36Sopenharmony_ci * the associated domain will be removed. 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_ci efc_sm_transition(&domain->drvsm, __efc_domain_init, 76162306a36Sopenharmony_ci NULL); 76262306a36Sopenharmony_ci efc_sm_post_event(&domain->drvsm, 76362306a36Sopenharmony_ci EFC_EVT_DOMAIN_FOUND, &drec); 76462306a36Sopenharmony_ci } else { 76562306a36Sopenharmony_ci domain->req_domain_free = true; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci default: 76962306a36Sopenharmony_ci __efc_domain_common_shutdown(__func__, ctx, evt, arg); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_civoid 77462306a36Sopenharmony_ci__efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx, 77562306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci std_domain_state_decl(); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci domain_sm_trace(domain); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* 78262306a36Sopenharmony_ci * Wait for the domain alloc/attach completion 78362306a36Sopenharmony_ci * after receiving a domain lost. 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci switch (evt) { 78662306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ALLOC_OK: 78762306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ATTACH_OK: { 78862306a36Sopenharmony_ci if (!list_empty(&domain->nport_list)) { 78962306a36Sopenharmony_ci /* 79062306a36Sopenharmony_ci * if there are nports, transition to 79162306a36Sopenharmony_ci * wait state and send shutdown to each nport 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci struct efc_nport *nport = NULL, *nport_next = NULL; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_nports_free, 79662306a36Sopenharmony_ci NULL); 79762306a36Sopenharmony_ci list_for_each_entry_safe(nport, nport_next, 79862306a36Sopenharmony_ci &domain->nport_list, 79962306a36Sopenharmony_ci list_entry) { 80062306a36Sopenharmony_ci efc_sm_post_event(&nport->sm, 80162306a36Sopenharmony_ci EFC_EVT_SHUTDOWN, NULL); 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci } else { 80462306a36Sopenharmony_ci /* no nports exist, free domain */ 80562306a36Sopenharmony_ci efc_sm_transition(ctx, __efc_domain_wait_shutdown, 80662306a36Sopenharmony_ci NULL); 80762306a36Sopenharmony_ci if (efc_cmd_domain_free(efc, domain)) 80862306a36Sopenharmony_ci efc_log_err(efc, "hw_domain_free() failed\n"); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ALLOC_FAIL: 81362306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ATTACH_FAIL: 81462306a36Sopenharmony_ci efc_log_err(efc, "[domain] %-20s: failed\n", 81562306a36Sopenharmony_ci efc_sm_event_name(evt)); 81662306a36Sopenharmony_ci break; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci default: 81962306a36Sopenharmony_ci __efc_domain_common_shutdown(__func__, ctx, evt, arg); 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_civoid 82462306a36Sopenharmony_ci__efc_domain_attach_internal(struct efc_domain *domain, u32 s_id) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci memcpy(domain->dma.virt, 82762306a36Sopenharmony_ci ((uint8_t *)domain->flogi_service_params) + 4, 82862306a36Sopenharmony_ci sizeof(struct fc_els_flogi) - 4); 82962306a36Sopenharmony_ci (void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH, 83062306a36Sopenharmony_ci &s_id); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_civoid 83462306a36Sopenharmony_ciefc_domain_attach(struct efc_domain *domain, u32 s_id) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci __efc_domain_attach_internal(domain, s_id); 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ciint 84062306a36Sopenharmony_ciefc_domain_post_event(struct efc_domain *domain, 84162306a36Sopenharmony_ci enum efc_sm_event event, void *arg) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci int rc; 84462306a36Sopenharmony_ci bool req_domain_free; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci rc = efc_sm_post_event(&domain->drvsm, event, arg); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci req_domain_free = domain->req_domain_free; 84962306a36Sopenharmony_ci domain->req_domain_free = false; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (req_domain_free) 85262306a36Sopenharmony_ci efc_domain_free(domain); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return rc; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic void 85862306a36Sopenharmony_ciefct_domain_process_pending(struct efc_domain *domain) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct efc *efc = domain->efc; 86162306a36Sopenharmony_ci struct efc_hw_sequence *seq = NULL; 86262306a36Sopenharmony_ci u32 processed = 0; 86362306a36Sopenharmony_ci unsigned long flags = 0; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci for (;;) { 86662306a36Sopenharmony_ci /* need to check for hold frames condition after each frame 86762306a36Sopenharmony_ci * processed because any given frame could cause a transition 86862306a36Sopenharmony_ci * to a state that holds frames 86962306a36Sopenharmony_ci */ 87062306a36Sopenharmony_ci if (efc->hold_frames) 87162306a36Sopenharmony_ci break; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Get next frame/sequence */ 87462306a36Sopenharmony_ci spin_lock_irqsave(&efc->pend_frames_lock, flags); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (!list_empty(&efc->pend_frames)) { 87762306a36Sopenharmony_ci seq = list_first_entry(&efc->pend_frames, 87862306a36Sopenharmony_ci struct efc_hw_sequence, list_entry); 87962306a36Sopenharmony_ci list_del(&seq->list_entry); 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (!seq) { 88362306a36Sopenharmony_ci processed = efc->pend_frames_processed; 88462306a36Sopenharmony_ci efc->pend_frames_processed = 0; 88562306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->pend_frames_lock, flags); 88662306a36Sopenharmony_ci break; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci efc->pend_frames_processed++; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->pend_frames_lock, flags); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* now dispatch frame(s) to dispatch function */ 89362306a36Sopenharmony_ci if (efc_domain_dispatch_frame(domain, seq)) 89462306a36Sopenharmony_ci efc->tt.hw_seq_free(efc, seq); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci seq = NULL; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (processed != 0) 90062306a36Sopenharmony_ci efc_log_debug(efc, "%u domain frames held and processed\n", 90162306a36Sopenharmony_ci processed); 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_civoid 90562306a36Sopenharmony_ciefc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct efc_domain *domain = efc->domain; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* 91062306a36Sopenharmony_ci * If we are holding frames or the domain is not yet registered or 91162306a36Sopenharmony_ci * there's already frames on the pending list, 91262306a36Sopenharmony_ci * then add the new frame to pending list 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_ci if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) { 91562306a36Sopenharmony_ci unsigned long flags = 0; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci spin_lock_irqsave(&efc->pend_frames_lock, flags); 91862306a36Sopenharmony_ci INIT_LIST_HEAD(&seq->list_entry); 91962306a36Sopenharmony_ci list_add_tail(&seq->list_entry, &efc->pend_frames); 92062306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->pend_frames_lock, flags); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (domain) { 92362306a36Sopenharmony_ci /* immediately process pending frames */ 92462306a36Sopenharmony_ci efct_domain_process_pending(domain); 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci } else { 92762306a36Sopenharmony_ci /* 92862306a36Sopenharmony_ci * We are not holding frames and pending list is empty, 92962306a36Sopenharmony_ci * just process frame. A non-zero return means the frame 93062306a36Sopenharmony_ci * was not handled - so cleanup 93162306a36Sopenharmony_ci */ 93262306a36Sopenharmony_ci if (efc_domain_dispatch_frame(domain, seq)) 93362306a36Sopenharmony_ci efc->tt.hw_seq_free(efc, seq); 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ciint 93862306a36Sopenharmony_ciefc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct efc_domain *domain = (struct efc_domain *)arg; 94162306a36Sopenharmony_ci struct efc *efc = domain->efc; 94262306a36Sopenharmony_ci struct fc_frame_header *hdr; 94362306a36Sopenharmony_ci struct efc_node *node = NULL; 94462306a36Sopenharmony_ci struct efc_nport *nport = NULL; 94562306a36Sopenharmony_ci unsigned long flags = 0; 94662306a36Sopenharmony_ci u32 s_id, d_id, rc = EFC_HW_SEQ_FREE; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) { 94962306a36Sopenharmony_ci efc_log_err(efc, "Sequence header or payload is null\n"); 95062306a36Sopenharmony_ci return rc; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci hdr = seq->header->dma.virt; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* extract the s_id and d_id */ 95662306a36Sopenharmony_ci s_id = ntoh24(hdr->fh_s_id); 95762306a36Sopenharmony_ci d_id = ntoh24(hdr->fh_d_id); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci nport = efc_nport_find(domain, d_id); 96262306a36Sopenharmony_ci if (!nport) { 96362306a36Sopenharmony_ci if (hdr->fh_type == FC_TYPE_FCP) { 96462306a36Sopenharmony_ci /* Drop frame */ 96562306a36Sopenharmony_ci efc_log_warn(efc, "FCP frame with invalid d_id x%x\n", 96662306a36Sopenharmony_ci d_id); 96762306a36Sopenharmony_ci goto out; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* p2p will use this case */ 97162306a36Sopenharmony_ci nport = domain->nport; 97262306a36Sopenharmony_ci if (!nport || !kref_get_unless_zero(&nport->ref)) { 97362306a36Sopenharmony_ci efc_log_err(efc, "Physical nport is NULL\n"); 97462306a36Sopenharmony_ci goto out; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* Lookup the node given the remote s_id */ 97962306a36Sopenharmony_ci node = efc_node_find(nport, s_id); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* If not found, then create a new node */ 98262306a36Sopenharmony_ci if (!node) { 98362306a36Sopenharmony_ci /* 98462306a36Sopenharmony_ci * If this is solicited data or control based on R_CTL and 98562306a36Sopenharmony_ci * there is no node context, then we can drop the frame 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_ci if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) || 98862306a36Sopenharmony_ci (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) { 98962306a36Sopenharmony_ci efc_log_debug(efc, "sol data/ctrl frame without node\n"); 99062306a36Sopenharmony_ci goto out_release; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci node = efc_node_alloc(nport, s_id, false, false); 99462306a36Sopenharmony_ci if (!node) { 99562306a36Sopenharmony_ci efc_log_err(efc, "efc_node_alloc() failed\n"); 99662306a36Sopenharmony_ci goto out_release; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci /* don't send PLOGI on efc_d_init entry */ 99962306a36Sopenharmony_ci efc_node_init_device(node, false); 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (node->hold_frames || !list_empty(&node->pend_frames)) { 100362306a36Sopenharmony_ci /* add frame to node's pending list */ 100462306a36Sopenharmony_ci spin_lock(&node->pend_frames_lock); 100562306a36Sopenharmony_ci INIT_LIST_HEAD(&seq->list_entry); 100662306a36Sopenharmony_ci list_add_tail(&seq->list_entry, &node->pend_frames); 100762306a36Sopenharmony_ci spin_unlock(&node->pend_frames_lock); 100862306a36Sopenharmony_ci rc = EFC_HW_SEQ_HOLD; 100962306a36Sopenharmony_ci goto out_release; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* now dispatch frame to the node frame handler */ 101362306a36Sopenharmony_ci efc_node_dispatch_frame(node, seq); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ciout_release: 101662306a36Sopenharmony_ci kref_put(&nport->ref, nport->release); 101762306a36Sopenharmony_ciout: 101862306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 101962306a36Sopenharmony_ci return rc; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_civoid 102362306a36Sopenharmony_ciefc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct fc_frame_header *hdr = seq->header->dma.virt; 102662306a36Sopenharmony_ci u32 port_id; 102762306a36Sopenharmony_ci struct efc_node *node = (struct efc_node *)arg; 102862306a36Sopenharmony_ci struct efc *efc = node->efc; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci port_id = ntoh24(hdr->fh_s_id); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (WARN_ON(port_id != node->rnode.fc_id)) 103362306a36Sopenharmony_ci return; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if ((!(ntoh24(hdr->fh_f_ctl) & FC_FC_END_SEQ)) || 103662306a36Sopenharmony_ci !(ntoh24(hdr->fh_f_ctl) & FC_FC_SEQ_INIT)) { 103762306a36Sopenharmony_ci node_printf(node, 103862306a36Sopenharmony_ci "Drop frame hdr = %08x %08x %08x %08x %08x %08x\n", 103962306a36Sopenharmony_ci cpu_to_be32(((u32 *)hdr)[0]), 104062306a36Sopenharmony_ci cpu_to_be32(((u32 *)hdr)[1]), 104162306a36Sopenharmony_ci cpu_to_be32(((u32 *)hdr)[2]), 104262306a36Sopenharmony_ci cpu_to_be32(((u32 *)hdr)[3]), 104362306a36Sopenharmony_ci cpu_to_be32(((u32 *)hdr)[4]), 104462306a36Sopenharmony_ci cpu_to_be32(((u32 *)hdr)[5])); 104562306a36Sopenharmony_ci return; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci switch (hdr->fh_r_ctl) { 104962306a36Sopenharmony_ci case FC_RCTL_ELS_REQ: 105062306a36Sopenharmony_ci case FC_RCTL_ELS_REP: 105162306a36Sopenharmony_ci efc_node_recv_els_frame(node, seq); 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci case FC_RCTL_BA_ABTS: 105562306a36Sopenharmony_ci case FC_RCTL_BA_ACC: 105662306a36Sopenharmony_ci case FC_RCTL_BA_RJT: 105762306a36Sopenharmony_ci case FC_RCTL_BA_NOP: 105862306a36Sopenharmony_ci efc_log_err(efc, "Received ABTS:\n"); 105962306a36Sopenharmony_ci break; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci case FC_RCTL_DD_UNSOL_CMD: 106262306a36Sopenharmony_ci case FC_RCTL_DD_UNSOL_CTL: 106362306a36Sopenharmony_ci switch (hdr->fh_type) { 106462306a36Sopenharmony_ci case FC_TYPE_FCP: 106562306a36Sopenharmony_ci if ((hdr->fh_r_ctl & 0xf) == FC_RCTL_DD_UNSOL_CMD) { 106662306a36Sopenharmony_ci if (!node->fcp_enabled) { 106762306a36Sopenharmony_ci efc_node_recv_fcp_cmd(node, seq); 106862306a36Sopenharmony_ci break; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci efc_log_err(efc, "Recvd FCP CMD. Drop IO\n"); 107162306a36Sopenharmony_ci } else if ((hdr->fh_r_ctl & 0xf) == 107262306a36Sopenharmony_ci FC_RCTL_DD_SOL_DATA) { 107362306a36Sopenharmony_ci node_printf(node, 107462306a36Sopenharmony_ci "solicited data recvd. Drop IO\n"); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci case FC_TYPE_CT: 107962306a36Sopenharmony_ci efc_node_recv_ct_frame(node, seq); 108062306a36Sopenharmony_ci break; 108162306a36Sopenharmony_ci default: 108262306a36Sopenharmony_ci break; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci break; 108562306a36Sopenharmony_ci default: 108662306a36Sopenharmony_ci efc_log_err(efc, "Unhandled frame rctl: %02x\n", hdr->fh_r_ctl); 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci} 1089