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#include "efc.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ciint 1062306a36Sopenharmony_ciefc_remote_node_cb(void *arg, int event, void *data) 1162306a36Sopenharmony_ci{ 1262306a36Sopenharmony_ci struct efc *efc = arg; 1362306a36Sopenharmony_ci struct efc_remote_node *rnode = data; 1462306a36Sopenharmony_ci struct efc_node *node = rnode->node; 1562306a36Sopenharmony_ci unsigned long flags = 0; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 1862306a36Sopenharmony_ci efc_node_post_event(node, event, NULL); 1962306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci return 0; 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct efc_node * 2562306a36Sopenharmony_ciefc_node_find(struct efc_nport *nport, u32 port_id) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci /* Find an FC node structure given the FC port ID */ 2862306a36Sopenharmony_ci return xa_load(&nport->lookup, port_id); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void 3262306a36Sopenharmony_ci_efc_node_free(struct kref *arg) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct efc_node *node = container_of(arg, struct efc_node, ref); 3562306a36Sopenharmony_ci struct efc *efc = node->efc; 3662306a36Sopenharmony_ci struct efc_dma *dma; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci dma = &node->sparm_dma_buf; 3962306a36Sopenharmony_ci dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); 4062306a36Sopenharmony_ci memset(dma, 0, sizeof(struct efc_dma)); 4162306a36Sopenharmony_ci mempool_free(node, efc->node_pool); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct efc_node *efc_node_alloc(struct efc_nport *nport, 4562306a36Sopenharmony_ci u32 port_id, bool init, bool targ) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int rc; 4862306a36Sopenharmony_ci struct efc_node *node = NULL; 4962306a36Sopenharmony_ci struct efc *efc = nport->efc; 5062306a36Sopenharmony_ci struct efc_dma *dma; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (nport->shutting_down) { 5362306a36Sopenharmony_ci efc_log_debug(efc, "node allocation when shutting down %06x", 5462306a36Sopenharmony_ci port_id); 5562306a36Sopenharmony_ci return NULL; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci node = mempool_alloc(efc->node_pool, GFP_ATOMIC); 5962306a36Sopenharmony_ci if (!node) { 6062306a36Sopenharmony_ci efc_log_err(efc, "node allocation failed %06x", port_id); 6162306a36Sopenharmony_ci return NULL; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci memset(node, 0, sizeof(*node)); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci dma = &node->sparm_dma_buf; 6662306a36Sopenharmony_ci dma->size = NODE_SPARAMS_SIZE; 6762306a36Sopenharmony_ci dma->virt = dma_pool_zalloc(efc->node_dma_pool, GFP_ATOMIC, &dma->phys); 6862306a36Sopenharmony_ci if (!dma->virt) { 6962306a36Sopenharmony_ci efc_log_err(efc, "node dma alloc failed\n"); 7062306a36Sopenharmony_ci goto dma_fail; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci node->rnode.indicator = U32_MAX; 7362306a36Sopenharmony_ci node->nport = nport; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci node->efc = efc; 7662306a36Sopenharmony_ci node->init = init; 7762306a36Sopenharmony_ci node->targ = targ; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci spin_lock_init(&node->pend_frames_lock); 8062306a36Sopenharmony_ci INIT_LIST_HEAD(&node->pend_frames); 8162306a36Sopenharmony_ci spin_lock_init(&node->els_ios_lock); 8262306a36Sopenharmony_ci INIT_LIST_HEAD(&node->els_ios_list); 8362306a36Sopenharmony_ci node->els_io_enabled = true; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci rc = efc_cmd_node_alloc(efc, &node->rnode, port_id, nport); 8662306a36Sopenharmony_ci if (rc) { 8762306a36Sopenharmony_ci efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc); 8862306a36Sopenharmony_ci goto hw_alloc_fail; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci node->rnode.node = node; 9262306a36Sopenharmony_ci node->sm.app = node; 9362306a36Sopenharmony_ci node->evtdepth = 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci efc_node_update_display_name(node); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci rc = xa_err(xa_store(&nport->lookup, port_id, node, GFP_ATOMIC)); 9862306a36Sopenharmony_ci if (rc) { 9962306a36Sopenharmony_ci efc_log_err(efc, "Node lookup store failed: %d\n", rc); 10062306a36Sopenharmony_ci goto xa_fail; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* initialize refcount */ 10462306a36Sopenharmony_ci kref_init(&node->ref); 10562306a36Sopenharmony_ci node->release = _efc_node_free; 10662306a36Sopenharmony_ci kref_get(&nport->ref); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return node; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cixa_fail: 11162306a36Sopenharmony_ci efc_node_free_resources(efc, &node->rnode); 11262306a36Sopenharmony_cihw_alloc_fail: 11362306a36Sopenharmony_ci dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); 11462306a36Sopenharmony_cidma_fail: 11562306a36Sopenharmony_ci mempool_free(node, efc->node_pool); 11662306a36Sopenharmony_ci return NULL; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_civoid 12062306a36Sopenharmony_ciefc_node_free(struct efc_node *node) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct efc_nport *nport; 12362306a36Sopenharmony_ci struct efc *efc; 12462306a36Sopenharmony_ci int rc = 0; 12562306a36Sopenharmony_ci struct efc_node *ns = NULL; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci nport = node->nport; 12862306a36Sopenharmony_ci efc = node->efc; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci node_printf(node, "Free'd\n"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (node->refound) { 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * Save the name server node. We will send fake RSCN event at 13562306a36Sopenharmony_ci * the end to handle ignored RSCN event during node deletion 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci ns = efc_node_find(node->nport, FC_FID_DIR_SERV); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!node->nport) { 14162306a36Sopenharmony_ci efc_log_err(efc, "Node already Freed\n"); 14262306a36Sopenharmony_ci return; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Free HW resources */ 14662306a36Sopenharmony_ci rc = efc_node_free_resources(efc, &node->rnode); 14762306a36Sopenharmony_ci if (rc < 0) 14862306a36Sopenharmony_ci efc_log_err(efc, "efc_hw_node_free failed: %d\n", rc); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* if the gidpt_delay_timer is still running, then delete it */ 15162306a36Sopenharmony_ci if (timer_pending(&node->gidpt_delay_timer)) 15262306a36Sopenharmony_ci del_timer(&node->gidpt_delay_timer); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci xa_erase(&nport->lookup, node->rnode.fc_id); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * If the node_list is empty, 15862306a36Sopenharmony_ci * then post a ALL_CHILD_NODES_FREE event to the nport, 15962306a36Sopenharmony_ci * after the lock is released. 16062306a36Sopenharmony_ci * The nport may be free'd as a result of the event. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci if (xa_empty(&nport->lookup)) 16362306a36Sopenharmony_ci efc_sm_post_event(&nport->sm, EFC_EVT_ALL_CHILD_NODES_FREE, 16462306a36Sopenharmony_ci NULL); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci node->nport = NULL; 16762306a36Sopenharmony_ci node->sm.current_state = NULL; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci kref_put(&nport->ref, nport->release); 17062306a36Sopenharmony_ci kref_put(&node->ref, node->release); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (ns) { 17362306a36Sopenharmony_ci /* sending fake RSCN event to name server node */ 17462306a36Sopenharmony_ci efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void 17962306a36Sopenharmony_ciefc_dma_copy_in(struct efc_dma *dma, void *buffer, u32 buffer_length) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci if (!dma || !buffer || !buffer_length) 18262306a36Sopenharmony_ci return; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (buffer_length > dma->size) 18562306a36Sopenharmony_ci buffer_length = dma->size; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci memcpy(dma->virt, buffer, buffer_length); 18862306a36Sopenharmony_ci dma->len = buffer_length; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ciint 19262306a36Sopenharmony_ciefc_node_attach(struct efc_node *node) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int rc = 0; 19562306a36Sopenharmony_ci struct efc_nport *nport = node->nport; 19662306a36Sopenharmony_ci struct efc_domain *domain = nport->domain; 19762306a36Sopenharmony_ci struct efc *efc = node->efc; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!domain->attached) { 20062306a36Sopenharmony_ci efc_log_err(efc, "Warning: unattached domain\n"); 20162306a36Sopenharmony_ci return -EIO; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci /* Update node->wwpn/wwnn */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn), 20662306a36Sopenharmony_ci efc_node_get_wwpn(node)); 20762306a36Sopenharmony_ci efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn), 20862306a36Sopenharmony_ci efc_node_get_wwnn(node)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4, 21162306a36Sopenharmony_ci sizeof(node->service_params) - 4); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* take lock to protect node->rnode.attached */ 21462306a36Sopenharmony_ci rc = efc_cmd_node_attach(efc, &node->rnode, &node->sparm_dma_buf); 21562306a36Sopenharmony_ci if (rc < 0) 21662306a36Sopenharmony_ci efc_log_debug(efc, "efc_hw_node_attach failed: %d\n", rc); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return rc; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_civoid 22262306a36Sopenharmony_ciefc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci switch (fc_id) { 22562306a36Sopenharmony_ci case FC_FID_FLOGI: 22662306a36Sopenharmony_ci snprintf(buffer, buffer_length, "fabric"); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case FC_FID_FCTRL: 22962306a36Sopenharmony_ci snprintf(buffer, buffer_length, "fabctl"); 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case FC_FID_DIR_SERV: 23262306a36Sopenharmony_ci snprintf(buffer, buffer_length, "nserve"); 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci default: 23562306a36Sopenharmony_ci if (fc_id == FC_FID_DOM_MGR) { 23662306a36Sopenharmony_ci snprintf(buffer, buffer_length, "dctl%02x", 23762306a36Sopenharmony_ci (fc_id & 0x0000ff)); 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci snprintf(buffer, buffer_length, "%06x", fc_id); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_civoid 24662306a36Sopenharmony_ciefc_node_update_display_name(struct efc_node *node) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci u32 port_id = node->rnode.fc_id; 24962306a36Sopenharmony_ci struct efc_nport *nport = node->nport; 25062306a36Sopenharmony_ci char portid_display[16]; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci efc_node_fcid_display(port_id, portid_display, sizeof(portid_display)); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci snprintf(node->display_name, sizeof(node->display_name), "%s.%s", 25562306a36Sopenharmony_ci nport->display_name, portid_display); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_civoid 25962306a36Sopenharmony_ciefc_node_send_ls_io_cleanup(struct efc_node *node) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) { 26262306a36Sopenharmony_ci efc_log_debug(node->efc, "[%s] cleaning up LS_ACC oxid=0x%x\n", 26362306a36Sopenharmony_ci node->display_name, node->ls_acc_oxid); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; 26662306a36Sopenharmony_ci node->ls_acc_io = NULL; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void efc_node_handle_implicit_logo(struct efc_node *node) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int rc; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * currently, only case for implicit logo is PLOGI 27662306a36Sopenharmony_ci * recvd. Thus, node's ELS IO pending list won't be 27762306a36Sopenharmony_ci * empty (PLOGI will be on it) 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI); 28062306a36Sopenharmony_ci node_printf(node, "Reason: implicit logout, re-authenticate\n"); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Re-attach node with the same HW node resources */ 28362306a36Sopenharmony_ci node->req_free = false; 28462306a36Sopenharmony_ci rc = efc_node_attach(node); 28562306a36Sopenharmony_ci efc_node_transition(node, __efc_d_wait_node_attach, NULL); 28662306a36Sopenharmony_ci node->els_io_enabled = true; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (rc < 0) 28962306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void efc_node_handle_explicit_logo(struct efc_node *node) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci s8 pend_frames_empty; 29562306a36Sopenharmony_ci unsigned long flags = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* cleanup any pending LS_ACC ELSs */ 29862306a36Sopenharmony_ci efc_node_send_ls_io_cleanup(node); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci spin_lock_irqsave(&node->pend_frames_lock, flags); 30162306a36Sopenharmony_ci pend_frames_empty = list_empty(&node->pend_frames); 30262306a36Sopenharmony_ci spin_unlock_irqrestore(&node->pend_frames_lock, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* 30562306a36Sopenharmony_ci * there are two scenarios where we want to keep 30662306a36Sopenharmony_ci * this node alive: 30762306a36Sopenharmony_ci * 1. there are pending frames that need to be 30862306a36Sopenharmony_ci * processed or 30962306a36Sopenharmony_ci * 2. we're an initiator and the remote node is 31062306a36Sopenharmony_ci * a target and we need to re-authenticate 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci node_printf(node, "Shutdown: explicit logo pend=%d ", !pend_frames_empty); 31362306a36Sopenharmony_ci node_printf(node, "nport.ini=%d node.tgt=%d\n", 31462306a36Sopenharmony_ci node->nport->enable_ini, node->targ); 31562306a36Sopenharmony_ci if (!pend_frames_empty || (node->nport->enable_ini && node->targ)) { 31662306a36Sopenharmony_ci u8 send_plogi = false; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (node->nport->enable_ini && node->targ) { 31962306a36Sopenharmony_ci /* 32062306a36Sopenharmony_ci * we're an initiator and 32162306a36Sopenharmony_ci * node shutting down is a target; 32262306a36Sopenharmony_ci * we'll need to re-authenticate in 32362306a36Sopenharmony_ci * initial state 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci send_plogi = true; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * transition to __efc_d_init 33062306a36Sopenharmony_ci * (will retain HW node resources) 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci node->els_io_enabled = true; 33362306a36Sopenharmony_ci node->req_free = false; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* 33662306a36Sopenharmony_ci * either pending frames exist or we are re-authenticating 33762306a36Sopenharmony_ci * with PLOGI (or both); in either case, return to initial 33862306a36Sopenharmony_ci * state 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci efc_node_init_device(node, send_plogi); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci /* else: let node shutdown occur */ 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void 34662306a36Sopenharmony_ciefc_node_purge_pending(struct efc_node *node) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct efc *efc = node->efc; 34962306a36Sopenharmony_ci struct efc_hw_sequence *frame, *next; 35062306a36Sopenharmony_ci unsigned long flags = 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci spin_lock_irqsave(&node->pend_frames_lock, flags); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci list_for_each_entry_safe(frame, next, &node->pend_frames, list_entry) { 35562306a36Sopenharmony_ci list_del(&frame->list_entry); 35662306a36Sopenharmony_ci efc->tt.hw_seq_free(efc, frame); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci spin_unlock_irqrestore(&node->pend_frames_lock, flags); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid 36362306a36Sopenharmony_ci__efc_node_shutdown(struct efc_sm_ctx *ctx, 36462306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct efc_node *node = ctx->app; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci efc_node_evt_set(ctx, evt, __func__); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci node_sm_trace(); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci switch (evt) { 37362306a36Sopenharmony_ci case EFC_EVT_ENTER: { 37462306a36Sopenharmony_ci efc_node_hold_frames(node); 37562306a36Sopenharmony_ci WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list)); 37662306a36Sopenharmony_ci /* by default, we will be freeing node after we unwind */ 37762306a36Sopenharmony_ci node->req_free = true; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci switch (node->shutdown_reason) { 38062306a36Sopenharmony_ci case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO: 38162306a36Sopenharmony_ci /* Node shutdown b/c of PLOGI received when node 38262306a36Sopenharmony_ci * already logged in. We have PLOGI service 38362306a36Sopenharmony_ci * parameters, so submit node attach; we won't be 38462306a36Sopenharmony_ci * freeing this node 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci efc_node_handle_implicit_logo(node); 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO: 39162306a36Sopenharmony_ci efc_node_handle_explicit_logo(node); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci case EFC_NODE_SHUTDOWN_DEFAULT: 39562306a36Sopenharmony_ci default: { 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci * shutdown due to link down, 39862306a36Sopenharmony_ci * node going away (xport event) or 39962306a36Sopenharmony_ci * nport shutdown, purge pending and 40062306a36Sopenharmony_ci * proceed to cleanup node 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* cleanup any pending LS_ACC ELSs */ 40462306a36Sopenharmony_ci efc_node_send_ls_io_cleanup(node); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci node_printf(node, 40762306a36Sopenharmony_ci "Shutdown reason: default, purge pending\n"); 40862306a36Sopenharmony_ci efc_node_purge_pending(node); 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci case EFC_EVT_EXIT: 41662306a36Sopenharmony_ci efc_node_accept_frames(node); 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci default: 42062306a36Sopenharmony_ci __efc_node_common(__func__, ctx, evt, arg); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic bool 42562306a36Sopenharmony_ciefc_node_check_els_quiesced(struct efc_node *node) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci /* check to see if ELS requests, completions are quiesced */ 42862306a36Sopenharmony_ci if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 && 42962306a36Sopenharmony_ci efc_els_io_list_empty(node, &node->els_ios_list)) { 43062306a36Sopenharmony_ci if (!node->attached) { 43162306a36Sopenharmony_ci /* hw node detach already completed, proceed */ 43262306a36Sopenharmony_ci node_printf(node, "HW node not attached\n"); 43362306a36Sopenharmony_ci efc_node_transition(node, 43462306a36Sopenharmony_ci __efc_node_wait_ios_shutdown, 43562306a36Sopenharmony_ci NULL); 43662306a36Sopenharmony_ci } else { 43762306a36Sopenharmony_ci /* 43862306a36Sopenharmony_ci * hw node detach hasn't completed, 43962306a36Sopenharmony_ci * transition and wait 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci node_printf(node, "HW node still attached\n"); 44262306a36Sopenharmony_ci efc_node_transition(node, __efc_node_wait_node_free, 44362306a36Sopenharmony_ci NULL); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci return true; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return false; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_civoid 45162306a36Sopenharmony_ciefc_node_initiate_cleanup(struct efc_node *node) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * if ELS's have already been quiesced, will move to next state 45562306a36Sopenharmony_ci * if ELS's have not been quiesced, abort them 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci if (!efc_node_check_els_quiesced(node)) { 45862306a36Sopenharmony_ci efc_node_hold_frames(node); 45962306a36Sopenharmony_ci efc_node_transition(node, __efc_node_wait_els_shutdown, NULL); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_civoid 46462306a36Sopenharmony_ci__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx, 46562306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci bool check_quiesce = false; 46862306a36Sopenharmony_ci struct efc_node *node = ctx->app; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci efc_node_evt_set(ctx, evt, __func__); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci node_sm_trace(); 47362306a36Sopenharmony_ci /* Node state machine: Wait for all ELSs to complete */ 47462306a36Sopenharmony_ci switch (evt) { 47562306a36Sopenharmony_ci case EFC_EVT_ENTER: 47662306a36Sopenharmony_ci efc_node_hold_frames(node); 47762306a36Sopenharmony_ci if (efc_els_io_list_empty(node, &node->els_ios_list)) { 47862306a36Sopenharmony_ci node_printf(node, "All ELS IOs complete\n"); 47962306a36Sopenharmony_ci check_quiesce = true; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci case EFC_EVT_EXIT: 48362306a36Sopenharmony_ci efc_node_accept_frames(node); 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_REQ_OK: 48762306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_REQ_FAIL: 48862306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_REQ_RJT: 48962306a36Sopenharmony_ci case EFC_EVT_ELS_REQ_ABORTED: 49062306a36Sopenharmony_ci if (WARN_ON(!node->els_req_cnt)) 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci node->els_req_cnt--; 49362306a36Sopenharmony_ci check_quiesce = true; 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_CMPL_OK: 49762306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_CMPL_FAIL: 49862306a36Sopenharmony_ci if (WARN_ON(!node->els_cmpl_cnt)) 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci node->els_cmpl_cnt--; 50162306a36Sopenharmony_ci check_quiesce = true; 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci case EFC_EVT_ALL_CHILD_NODES_FREE: 50562306a36Sopenharmony_ci /* all ELS IO's complete */ 50662306a36Sopenharmony_ci node_printf(node, "All ELS IOs complete\n"); 50762306a36Sopenharmony_ci WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list)); 50862306a36Sopenharmony_ci check_quiesce = true; 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: 51262306a36Sopenharmony_ci check_quiesce = true; 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ATTACH_OK: 51662306a36Sopenharmony_ci /* don't care about domain_attach_ok */ 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* ignore shutdown events as we're already in shutdown path */ 52062306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN: 52162306a36Sopenharmony_ci /* have default shutdown event take precedence */ 52262306a36Sopenharmony_ci node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; 52362306a36Sopenharmony_ci fallthrough; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: 52662306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: 52762306a36Sopenharmony_ci node_printf(node, "%s received\n", efc_sm_event_name(evt)); 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci default: 53162306a36Sopenharmony_ci __efc_node_common(__func__, ctx, evt, arg); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (check_quiesce) 53562306a36Sopenharmony_ci efc_node_check_els_quiesced(node); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_civoid 53962306a36Sopenharmony_ci__efc_node_wait_node_free(struct efc_sm_ctx *ctx, 54062306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct efc_node *node = ctx->app; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci efc_node_evt_set(ctx, evt, __func__); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci node_sm_trace(); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (evt) { 54962306a36Sopenharmony_ci case EFC_EVT_ENTER: 55062306a36Sopenharmony_ci efc_node_hold_frames(node); 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci case EFC_EVT_EXIT: 55462306a36Sopenharmony_ci efc_node_accept_frames(node); 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci case EFC_EVT_NODE_FREE_OK: 55862306a36Sopenharmony_ci /* node is officially no longer attached */ 55962306a36Sopenharmony_ci node->attached = false; 56062306a36Sopenharmony_ci efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL); 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci case EFC_EVT_ALL_CHILD_NODES_FREE: 56462306a36Sopenharmony_ci case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: 56562306a36Sopenharmony_ci /* As IOs and ELS IO's complete we expect to get these events */ 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ATTACH_OK: 56962306a36Sopenharmony_ci /* don't care about domain_attach_ok */ 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* ignore shutdown events as we're already in shutdown path */ 57362306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN: 57462306a36Sopenharmony_ci /* have default shutdown event take precedence */ 57562306a36Sopenharmony_ci node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; 57662306a36Sopenharmony_ci fallthrough; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: 57962306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: 58062306a36Sopenharmony_ci node_printf(node, "%s received\n", efc_sm_event_name(evt)); 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci default: 58362306a36Sopenharmony_ci __efc_node_common(__func__, ctx, evt, arg); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_civoid 58862306a36Sopenharmony_ci__efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx, 58962306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct efc_node *node = ctx->app; 59262306a36Sopenharmony_ci struct efc *efc = node->efc; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci efc_node_evt_set(ctx, evt, __func__); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci node_sm_trace(); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci switch (evt) { 59962306a36Sopenharmony_ci case EFC_EVT_ENTER: 60062306a36Sopenharmony_ci efc_node_hold_frames(node); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* first check to see if no ELS IOs are outstanding */ 60362306a36Sopenharmony_ci if (efc_els_io_list_empty(node, &node->els_ios_list)) 60462306a36Sopenharmony_ci /* If there are any active IOS, Free them. */ 60562306a36Sopenharmony_ci efc_node_transition(node, __efc_node_shutdown, NULL); 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: 60962306a36Sopenharmony_ci case EFC_EVT_ALL_CHILD_NODES_FREE: 61062306a36Sopenharmony_ci if (efc_els_io_list_empty(node, &node->els_ios_list)) 61162306a36Sopenharmony_ci efc_node_transition(node, __efc_node_shutdown, NULL); 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci case EFC_EVT_EXIT: 61562306a36Sopenharmony_ci efc_node_accept_frames(node); 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_REQ_FAIL: 61962306a36Sopenharmony_ci /* Can happen as ELS IO IO's complete */ 62062306a36Sopenharmony_ci if (WARN_ON(!node->els_req_cnt)) 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci node->els_req_cnt--; 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* ignore shutdown events as we're already in shutdown path */ 62662306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN: 62762306a36Sopenharmony_ci /* have default shutdown event take precedence */ 62862306a36Sopenharmony_ci node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; 62962306a36Sopenharmony_ci fallthrough; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: 63262306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: 63362306a36Sopenharmony_ci efc_log_debug(efc, "[%s] %-20s\n", node->display_name, 63462306a36Sopenharmony_ci efc_sm_event_name(evt)); 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ATTACH_OK: 63762306a36Sopenharmony_ci /* don't care about domain_attach_ok */ 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci default: 64062306a36Sopenharmony_ci __efc_node_common(__func__, ctx, evt, arg); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_civoid 64562306a36Sopenharmony_ci__efc_node_common(const char *funcname, struct efc_sm_ctx *ctx, 64662306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct efc_node *node = NULL; 64962306a36Sopenharmony_ci struct efc *efc = NULL; 65062306a36Sopenharmony_ci struct efc_node_cb *cbdata = arg; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci node = ctx->app; 65362306a36Sopenharmony_ci efc = node->efc; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci switch (evt) { 65662306a36Sopenharmony_ci case EFC_EVT_ENTER: 65762306a36Sopenharmony_ci case EFC_EVT_REENTER: 65862306a36Sopenharmony_ci case EFC_EVT_EXIT: 65962306a36Sopenharmony_ci case EFC_EVT_NPORT_TOPOLOGY_NOTIFY: 66062306a36Sopenharmony_ci case EFC_EVT_NODE_MISSING: 66162306a36Sopenharmony_ci case EFC_EVT_FCP_CMD_RCVD: 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci case EFC_EVT_NODE_REFOUND: 66562306a36Sopenharmony_ci node->refound = true; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* 66962306a36Sopenharmony_ci * node->attached must be set appropriately 67062306a36Sopenharmony_ci * for all node attach/detach events 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_ci case EFC_EVT_NODE_ATTACH_OK: 67362306a36Sopenharmony_ci node->attached = true; 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci case EFC_EVT_NODE_FREE_OK: 67762306a36Sopenharmony_ci case EFC_EVT_NODE_ATTACH_FAIL: 67862306a36Sopenharmony_ci node->attached = false; 67962306a36Sopenharmony_ci break; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* 68262306a36Sopenharmony_ci * handle any ELS completions that 68362306a36Sopenharmony_ci * other states either didn't care about 68462306a36Sopenharmony_ci * or forgot about 68562306a36Sopenharmony_ci */ 68662306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_CMPL_OK: 68762306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_CMPL_FAIL: 68862306a36Sopenharmony_ci if (WARN_ON(!node->els_cmpl_cnt)) 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci node->els_cmpl_cnt--; 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* 69462306a36Sopenharmony_ci * handle any ELS request completions that 69562306a36Sopenharmony_ci * other states either didn't care about 69662306a36Sopenharmony_ci * or forgot about 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_REQ_OK: 69962306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_REQ_FAIL: 70062306a36Sopenharmony_ci case EFC_EVT_SRRS_ELS_REQ_RJT: 70162306a36Sopenharmony_ci case EFC_EVT_ELS_REQ_ABORTED: 70262306a36Sopenharmony_ci if (WARN_ON(!node->els_req_cnt)) 70362306a36Sopenharmony_ci break; 70462306a36Sopenharmony_ci node->els_req_cnt--; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci case EFC_EVT_ELS_RCVD: { 70862306a36Sopenharmony_ci struct fc_frame_header *hdr = cbdata->header->dma.virt; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* 71162306a36Sopenharmony_ci * Unsupported ELS was received, 71262306a36Sopenharmony_ci * send LS_RJT, command not supported 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ci efc_log_debug(efc, 71562306a36Sopenharmony_ci "[%s] (%s) ELS x%02x, LS_RJT not supported\n", 71662306a36Sopenharmony_ci node->display_name, funcname, 71762306a36Sopenharmony_ci ((u8 *)cbdata->payload->dma.virt)[0]); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), 72062306a36Sopenharmony_ci ELS_RJT_UNSUP, ELS_EXPL_NONE, 0); 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci case EFC_EVT_PLOGI_RCVD: 72562306a36Sopenharmony_ci case EFC_EVT_FLOGI_RCVD: 72662306a36Sopenharmony_ci case EFC_EVT_LOGO_RCVD: 72762306a36Sopenharmony_ci case EFC_EVT_PRLI_RCVD: 72862306a36Sopenharmony_ci case EFC_EVT_PRLO_RCVD: 72962306a36Sopenharmony_ci case EFC_EVT_PDISC_RCVD: 73062306a36Sopenharmony_ci case EFC_EVT_FDISC_RCVD: 73162306a36Sopenharmony_ci case EFC_EVT_ADISC_RCVD: 73262306a36Sopenharmony_ci case EFC_EVT_RSCN_RCVD: 73362306a36Sopenharmony_ci case EFC_EVT_SCR_RCVD: { 73462306a36Sopenharmony_ci struct fc_frame_header *hdr = cbdata->header->dma.virt; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* sm: / send ELS_RJT */ 73762306a36Sopenharmony_ci efc_log_debug(efc, "[%s] (%s) %s sending ELS_RJT\n", 73862306a36Sopenharmony_ci node->display_name, funcname, 73962306a36Sopenharmony_ci efc_sm_event_name(evt)); 74062306a36Sopenharmony_ci /* if we didn't catch this in a state, send generic LS_RJT */ 74162306a36Sopenharmony_ci efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), 74262306a36Sopenharmony_ci ELS_RJT_UNAB, ELS_EXPL_NONE, 0); 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci case EFC_EVT_ABTS_RCVD: { 74662306a36Sopenharmony_ci efc_log_debug(efc, "[%s] (%s) %s sending BA_ACC\n", 74762306a36Sopenharmony_ci node->display_name, funcname, 74862306a36Sopenharmony_ci efc_sm_event_name(evt)); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* sm: / send BA_ACC */ 75162306a36Sopenharmony_ci efc_send_bls_acc(node, cbdata->header->dma.virt); 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci default: 75662306a36Sopenharmony_ci efc_log_debug(node->efc, "[%s] %-20s %-20s not handled\n", 75762306a36Sopenharmony_ci node->display_name, funcname, 75862306a36Sopenharmony_ci efc_sm_event_name(evt)); 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_civoid 76362306a36Sopenharmony_ciefc_node_save_sparms(struct efc_node *node, void *payload) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci memcpy(node->service_params, payload, sizeof(node->service_params)); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_civoid 76962306a36Sopenharmony_ciefc_node_post_event(struct efc_node *node, 77062306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci bool free_node = false; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci node->evtdepth++; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci efc_sm_post_event(&node->sm, evt, arg); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* If our event call depth is one and 77962306a36Sopenharmony_ci * we're not holding frames 78062306a36Sopenharmony_ci * then we can dispatch any pending frames. 78162306a36Sopenharmony_ci * We don't want to allow the efc_process_node_pending() 78262306a36Sopenharmony_ci * call to recurse. 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_ci if (!node->hold_frames && node->evtdepth == 1) 78562306a36Sopenharmony_ci efc_process_node_pending(node); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci node->evtdepth--; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* 79062306a36Sopenharmony_ci * Free the node object if so requested, 79162306a36Sopenharmony_ci * and we're at an event call depth of zero 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci if (node->evtdepth == 0 && node->req_free) 79462306a36Sopenharmony_ci free_node = true; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (free_node) 79762306a36Sopenharmony_ci efc_node_free(node); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_civoid 80162306a36Sopenharmony_ciefc_node_transition(struct efc_node *node, 80262306a36Sopenharmony_ci void (*state)(struct efc_sm_ctx *, 80362306a36Sopenharmony_ci enum efc_sm_event, void *), void *data) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct efc_sm_ctx *ctx = &node->sm; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (ctx->current_state == state) { 80862306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_REENTER, data); 80962306a36Sopenharmony_ci } else { 81062306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_EXIT, data); 81162306a36Sopenharmony_ci ctx->current_state = state; 81262306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_ENTER, data); 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_civoid 81762306a36Sopenharmony_ciefc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci memset(buf, 0, buf_len); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci snprintf(buf, buf_len, "eui.%016llX", (unsigned long long)eui_name); 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ciu64 82562306a36Sopenharmony_ciefc_node_get_wwpn(struct efc_node *node) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct fc_els_flogi *sp = 82862306a36Sopenharmony_ci (struct fc_els_flogi *)node->service_params; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return be64_to_cpu(sp->fl_wwpn); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ciu64 83462306a36Sopenharmony_ciefc_node_get_wwnn(struct efc_node *node) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct fc_els_flogi *sp = 83762306a36Sopenharmony_ci (struct fc_els_flogi *)node->service_params; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return be64_to_cpu(sp->fl_wwnn); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ciint 84362306a36Sopenharmony_ciefc_node_check_els_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg, 84462306a36Sopenharmony_ci u8 cmd, void (*efc_node_common_func)(const char *, 84562306a36Sopenharmony_ci struct efc_sm_ctx *, enum efc_sm_event, void *), 84662306a36Sopenharmony_ci const char *funcname) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ciint 85262306a36Sopenharmony_ciefc_node_check_ns_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg, 85362306a36Sopenharmony_ci u16 cmd, void (*efc_node_common_func)(const char *, 85462306a36Sopenharmony_ci struct efc_sm_ctx *, enum efc_sm_event, void *), 85562306a36Sopenharmony_ci const char *funcname) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci return 0; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ciint 86162306a36Sopenharmony_ciefc_els_io_list_empty(struct efc_node *node, struct list_head *list) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci int empty; 86462306a36Sopenharmony_ci unsigned long flags = 0; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci spin_lock_irqsave(&node->els_ios_lock, flags); 86762306a36Sopenharmony_ci empty = list_empty(list); 86862306a36Sopenharmony_ci spin_unlock_irqrestore(&node->els_ios_lock, flags); 86962306a36Sopenharmony_ci return empty; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_civoid 87362306a36Sopenharmony_ciefc_node_pause(struct efc_node *node, 87462306a36Sopenharmony_ci void (*state)(struct efc_sm_ctx *, 87562306a36Sopenharmony_ci enum efc_sm_event, void *)) 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci node->nodedb_state = state; 87962306a36Sopenharmony_ci efc_node_transition(node, __efc_node_paused, NULL); 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_civoid 88362306a36Sopenharmony_ci__efc_node_paused(struct efc_sm_ctx *ctx, 88462306a36Sopenharmony_ci enum efc_sm_event evt, void *arg) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci struct efc_node *node = ctx->app; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci efc_node_evt_set(ctx, evt, __func__); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci node_sm_trace(); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* 89362306a36Sopenharmony_ci * This state is entered when a state is "paused". When resumed, the 89462306a36Sopenharmony_ci * node is transitioned to a previously saved state (node->ndoedb_state) 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_ci switch (evt) { 89762306a36Sopenharmony_ci case EFC_EVT_ENTER: 89862306a36Sopenharmony_ci node_printf(node, "Paused\n"); 89962306a36Sopenharmony_ci break; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci case EFC_EVT_RESUME: { 90262306a36Sopenharmony_ci void (*pf)(struct efc_sm_ctx *ctx, 90362306a36Sopenharmony_ci enum efc_sm_event evt, void *arg); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci pf = node->nodedb_state; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci node->nodedb_state = NULL; 90862306a36Sopenharmony_ci efc_node_transition(node, pf, NULL); 90962306a36Sopenharmony_ci break; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci case EFC_EVT_DOMAIN_ATTACH_OK: 91362306a36Sopenharmony_ci break; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci case EFC_EVT_SHUTDOWN: 91662306a36Sopenharmony_ci node->req_free = true; 91762306a36Sopenharmony_ci break; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci default: 92062306a36Sopenharmony_ci __efc_node_common(__func__, ctx, evt, arg); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_civoid 92562306a36Sopenharmony_ciefc_node_recv_els_frame(struct efc_node *node, 92662306a36Sopenharmony_ci struct efc_hw_sequence *seq) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci u32 prli_size = sizeof(struct fc_els_prli) + sizeof(struct fc_els_spp); 92962306a36Sopenharmony_ci struct { 93062306a36Sopenharmony_ci u32 cmd; 93162306a36Sopenharmony_ci enum efc_sm_event evt; 93262306a36Sopenharmony_ci u32 payload_size; 93362306a36Sopenharmony_ci } els_cmd_list[] = { 93462306a36Sopenharmony_ci {ELS_PLOGI, EFC_EVT_PLOGI_RCVD, sizeof(struct fc_els_flogi)}, 93562306a36Sopenharmony_ci {ELS_FLOGI, EFC_EVT_FLOGI_RCVD, sizeof(struct fc_els_flogi)}, 93662306a36Sopenharmony_ci {ELS_LOGO, EFC_EVT_LOGO_RCVD, sizeof(struct fc_els_ls_acc)}, 93762306a36Sopenharmony_ci {ELS_PRLI, EFC_EVT_PRLI_RCVD, prli_size}, 93862306a36Sopenharmony_ci {ELS_PRLO, EFC_EVT_PRLO_RCVD, prli_size}, 93962306a36Sopenharmony_ci {ELS_PDISC, EFC_EVT_PDISC_RCVD, MAX_ACC_REJECT_PAYLOAD}, 94062306a36Sopenharmony_ci {ELS_FDISC, EFC_EVT_FDISC_RCVD, MAX_ACC_REJECT_PAYLOAD}, 94162306a36Sopenharmony_ci {ELS_ADISC, EFC_EVT_ADISC_RCVD, sizeof(struct fc_els_adisc)}, 94262306a36Sopenharmony_ci {ELS_RSCN, EFC_EVT_RSCN_RCVD, MAX_ACC_REJECT_PAYLOAD}, 94362306a36Sopenharmony_ci {ELS_SCR, EFC_EVT_SCR_RCVD, MAX_ACC_REJECT_PAYLOAD}, 94462306a36Sopenharmony_ci }; 94562306a36Sopenharmony_ci struct efc_node_cb cbdata; 94662306a36Sopenharmony_ci u8 *buf = seq->payload->dma.virt; 94762306a36Sopenharmony_ci enum efc_sm_event evt = EFC_EVT_ELS_RCVD; 94862306a36Sopenharmony_ci u32 i; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci memset(&cbdata, 0, sizeof(cbdata)); 95162306a36Sopenharmony_ci cbdata.header = seq->header; 95262306a36Sopenharmony_ci cbdata.payload = seq->payload; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* find a matching event for the ELS command */ 95562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(els_cmd_list); i++) { 95662306a36Sopenharmony_ci if (els_cmd_list[i].cmd == buf[0]) { 95762306a36Sopenharmony_ci evt = els_cmd_list[i].evt; 95862306a36Sopenharmony_ci break; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci efc_node_post_event(node, evt, &cbdata); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_civoid 96662306a36Sopenharmony_ciefc_node_recv_ct_frame(struct efc_node *node, 96762306a36Sopenharmony_ci struct efc_hw_sequence *seq) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci struct fc_ct_hdr *iu = seq->payload->dma.virt; 97062306a36Sopenharmony_ci struct fc_frame_header *hdr = seq->header->dma.virt; 97162306a36Sopenharmony_ci struct efc *efc = node->efc; 97262306a36Sopenharmony_ci u16 gscmd = be16_to_cpu(iu->ct_cmd); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci efc_log_err(efc, "[%s] Received cmd :%x sending CT_REJECT\n", 97562306a36Sopenharmony_ci node->display_name, gscmd); 97662306a36Sopenharmony_ci efc_send_ct_rsp(efc, node, be16_to_cpu(hdr->fh_ox_id), iu, 97762306a36Sopenharmony_ci FC_FS_RJT, FC_FS_RJT_UNSUP, 0); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_civoid 98162306a36Sopenharmony_ciefc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct efc_node_cb cbdata; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci memset(&cbdata, 0, sizeof(cbdata)); 98662306a36Sopenharmony_ci cbdata.header = seq->header; 98762306a36Sopenharmony_ci cbdata.payload = seq->payload; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_FCP_CMD_RCVD, &cbdata); 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_civoid 99362306a36Sopenharmony_ciefc_process_node_pending(struct efc_node *node) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct efc *efc = node->efc; 99662306a36Sopenharmony_ci struct efc_hw_sequence *seq = NULL; 99762306a36Sopenharmony_ci u32 pend_frames_processed = 0; 99862306a36Sopenharmony_ci unsigned long flags = 0; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci for (;;) { 100162306a36Sopenharmony_ci /* need to check for hold frames condition after each frame 100262306a36Sopenharmony_ci * processed because any given frame could cause a transition 100362306a36Sopenharmony_ci * to a state that holds frames 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_ci if (node->hold_frames) 100662306a36Sopenharmony_ci break; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci seq = NULL; 100962306a36Sopenharmony_ci /* Get next frame/sequence */ 101062306a36Sopenharmony_ci spin_lock_irqsave(&node->pend_frames_lock, flags); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (!list_empty(&node->pend_frames)) { 101362306a36Sopenharmony_ci seq = list_first_entry(&node->pend_frames, 101462306a36Sopenharmony_ci struct efc_hw_sequence, list_entry); 101562306a36Sopenharmony_ci list_del(&seq->list_entry); 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci spin_unlock_irqrestore(&node->pend_frames_lock, flags); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (!seq) { 102062306a36Sopenharmony_ci pend_frames_processed = node->pend_frames_processed; 102162306a36Sopenharmony_ci node->pend_frames_processed = 0; 102262306a36Sopenharmony_ci break; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci node->pend_frames_processed++; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci /* now dispatch frame(s) to dispatch function */ 102762306a36Sopenharmony_ci efc_node_dispatch_frame(node, seq); 102862306a36Sopenharmony_ci efc->tt.hw_seq_free(efc, seq); 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (pend_frames_processed != 0) 103262306a36Sopenharmony_ci efc_log_debug(efc, "%u node frames held and processed\n", 103362306a36Sopenharmony_ci pend_frames_processed); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_civoid 103762306a36Sopenharmony_ciefc_scsi_sess_reg_complete(struct efc_node *node, u32 status) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci unsigned long flags = 0; 104062306a36Sopenharmony_ci enum efc_sm_event evt = EFC_EVT_NODE_SESS_REG_OK; 104162306a36Sopenharmony_ci struct efc *efc = node->efc; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (status) 104462306a36Sopenharmony_ci evt = EFC_EVT_NODE_SESS_REG_FAIL; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 104762306a36Sopenharmony_ci /* Notify the node to resume */ 104862306a36Sopenharmony_ci efc_node_post_event(node, evt, NULL); 104962306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_civoid 105362306a36Sopenharmony_ciefc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci unsigned long flags = 0; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 105862306a36Sopenharmony_ci /* Notify the node to resume */ 105962306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_NODE_DEL_INI_COMPLETE, NULL); 106062306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_civoid 106462306a36Sopenharmony_ciefc_scsi_del_target_complete(struct efc *efc, struct efc_node *node) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci unsigned long flags = 0; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 106962306a36Sopenharmony_ci /* Notify the node to resume */ 107062306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL); 107162306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_civoid 107562306a36Sopenharmony_ciefc_scsi_io_list_empty(struct efc *efc, struct efc_node *node) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci unsigned long flags = 0; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 108062306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL); 108162306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_civoid efc_node_post_els_resp(struct efc_node *node, u32 evt, void *arg) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct efc *efc = node->efc; 108762306a36Sopenharmony_ci unsigned long flags = 0; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 109062306a36Sopenharmony_ci efc_node_post_event(node, evt, arg); 109162306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_civoid efc_node_post_shutdown(struct efc_node *node, void *arg) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci unsigned long flags = 0; 109762306a36Sopenharmony_ci struct efc *efc = node->efc; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci spin_lock_irqsave(&efc->lock, flags); 110062306a36Sopenharmony_ci efc_node_post_event(node, EFC_EVT_SHUTDOWN, arg); 110162306a36Sopenharmony_ci spin_unlock_irqrestore(&efc->lock, flags); 110262306a36Sopenharmony_ci} 1103