// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2021 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. */ #include "efc.h" intefc_remote_node_cb(void *arg, int event, void *data){ struct efc *efc = arg; struct efc_remote_node *rnode = data; struct efc_node *node = rnode->node; unsigned long flags = 0; spin_lock_irqsave(&efc->lock, flags); efc_node_post_event(node, event, NULL); spin_unlock_irqrestore(&efc->lock, flags); return 0;} struct efc_node *efc_node_find(struct efc_nport *nport, u32 port_id){ /* Find an FC node structure given the FC port ID */ return xa_load(&nport->lookup, port_id);} static void_efc_node_free(struct kref *arg){ struct efc_node *node = container_of(arg, struct efc_node, ref); struct efc *efc = node->efc; struct efc_dma *dma; dma = &node->sparm_dma_buf; dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); memset(dma, 0, sizeof(struct efc_dma)); mempool_free(node, efc->node_pool);} struct efc_node *efc_node_alloc(struct efc_nport *nport, u32 port_id, bool init, bool targ){ int rc; struct efc_node *node = NULL; struct efc *efc = nport->efc; struct efc_dma *dma; if (nport->shutting_down) { efc_log_debug(efc, "node allocation when shutting down %06x", port_id); return NULL; } node = mempool_alloc(efc->node_pool, GFP_ATOMIC); if (!node) { efc_log_err(efc, "node allocation failed %06x", port_id); return NULL; } memset(node, 0, sizeof(*node)); dma = &node->sparm_dma_buf; dma->size = NODE_SPARAMS_SIZE; dma->virt = dma_pool_zalloc(efc->node_dma_pool, GFP_ATOMIC, &dma->phys); if (!dma->virt) { efc_log_err(efc, "node dma alloc failed\n"); goto dma_fail; } node->rnode.indicator = U32_MAX; node->nport = nport; node->efc = efc; node->init = init; node->targ = targ; spin_lock_init(&node->pend_frames_lock); INIT_LIST_HEAD(&node->pend_frames); spin_lock_init(&node->els_ios_lock); INIT_LIST_HEAD(&node->els_ios_list); node->els_io_enabled = true; rc = efc_cmd_node_alloc(efc, &node->rnode, port_id, nport); if (rc) { efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc); goto hw_alloc_fail; } node->rnode.node = node; node->sm.app = node; node->evtdepth = 0; efc_node_update_display_name(node); rc = xa_err(xa_store(&nport->lookup, port_id, node, GFP_ATOMIC)); if (rc) { efc_log_err(efc, "Node lookup store failed: %d\n", rc); goto xa_fail; } /* initialize refcount */ kref_init(&node->ref); node->release = _efc_node_free; kref_get(&nport->ref); return node; xa_fail: efc_node_free_resources(efc, &node->rnode);hw_alloc_fail: dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys);dma_fail: mempool_free(node, efc->node_pool); return NULL;} voidefc_node_free(struct efc_node *node){ struct efc_nport *nport; struct efc *efc; int rc = 0; struct efc_node *ns = NULL; nport = node->nport; efc = node->efc; node_printf(node, "Free'd\n"); if (node->refound) { /* * Save the name server node. We will send fake RSCN event at * the end to handle ignored RSCN event during node deletion */ ns = efc_node_find(node->nport, FC_FID_DIR_SERV); } if (!node->nport) { efc_log_err(efc, "Node already Freed\n"); return; } /* Free HW resources */ rc = efc_node_free_resources(efc, &node->rnode); if (rc < 0) efc_log_err(efc, "efc_hw_node_free failed: %d\n", rc); /* if the gidpt_delay_timer is still running, then delete it */ if (timer_pending(&node->gidpt_delay_timer)) timer_delete(&node->gidpt_delay_timer); xa_erase(&nport->lookup, node->rnode.fc_id); /* * If the node_list is empty, * then post a ALL_CHILD_NODES_FREE event to the nport, * after the lock is released. * The nport may be free'd as a result of the event. */ if (xa_empty(&nport->lookup)) efc_sm_post_event(&nport->sm, EFC_EVT_ALL_CHILD_NODES_FREE, NULL); node->nport = NULL; node->sm.current_state = NULL; kref_put(&nport->ref, nport->release); kref_put(&node->ref, node->release); if (ns) { /* sending fake RSCN event to name server node */ efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL); }} static voidefc_dma_copy_in(struct efc_dma *dma, void *buffer, u32 buffer_length){ if (!dma || !buffer || !buffer_length) return; if (buffer_length > dma->size) buffer_length = dma->size; memcpy(dma->virt, buffer, buffer_length); dma->len = buffer_length;} intefc_node_attach(struct efc_node *node){ int rc = 0; struct efc_nport *nport = node->nport; struct efc_domain *domain = nport->domain; struct efc *efc = node->efc; if (!domain->attached) { efc_log_err(efc, "Warning: unattached domain\n"); return -EIO; } /* Update node->wwpn/wwnn */ efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn), efc_node_get_wwpn(node)); efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn), efc_node_get_wwnn(node)); efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4, sizeof(node->service_params) - 4); /* take lock to protect node->rnode.attached */ rc = efc_cmd_node_attach(efc, &node->rnode, &node->sparm_dma_buf); if (rc < 0) efc_log_debug(efc, "efc_hw_node_attach failed: %d\n", rc); return rc;} voidefc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length){ switch (fc_id) { case FC_FID_FLOGI: snprintf(buffer, buffer_length, "fabric"); break; case FC_FID_FCTRL: snprintf(buffer, buffer_length, "fabctl"); break; case FC_FID_DIR_SERV: snprintf(buffer, buffer_length, "nserve"); break; default: if (fc_id == FC_FID_DOM_MGR) { snprintf(buffer, buffer_length, "dctl%02x", (fc_id & 0x0000ff)); } else { snprintf(buffer, buffer_length, "%06x", fc_id); } break; }} voidefc_node_update_display_name(struct efc_node *node){ u32 port_id = node->rnode.fc_id; struct efc_nport *nport = node->nport; char portid_display[16]; efc_node_fcid_display(port_id, portid_display, sizeof(portid_display)); snprintf(node->display_name, sizeof(node->display_name), "%s.%s", nport->display_name, portid_display);} voidefc_node_send_ls_io_cleanup(struct efc_node *node){ if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) { efc_log_debug(node->efc, "[%s] cleaning up LS_ACC oxid=0x%x\n", node->display_name, node->ls_acc_oxid); node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; node->ls_acc_io = NULL; }} static void efc_node_handle_implicit_logo(struct efc_node *node){ int rc; /* * currently, only case for implicit logo is PLOGI * recvd. Thus, node's ELS IO pending list won't be * empty (PLOGI will be on it) */ WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI); node_printf(node, "Reason: implicit logout, re-authenticate\n"); /* Re-attach node with the same HW node resources */ node->req_free = false; rc = efc_node_attach(node); efc_node_transition(node, __efc_d_wait_node_attach, NULL); node->els_io_enabled = true; if (rc < 0) efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL);} static void efc_node_handle_explicit_logo(struct efc_node *node){ s8 pend_frames_empty; unsigned long flags = 0; /* cleanup any pending LS_ACC ELSs */ efc_node_send_ls_io_cleanup(node); spin_lock_irqsave(&node->pend_frames_lock, flags); pend_frames_empty = list_empty(&node->pend_frames); spin_unlock_irqrestore(&node->pend_frames_lock, flags); /* * there are two scenarios where we want to keep * this node alive: * 1. there are pending frames that need to be * processed or * 2. we're an initiator and the remote node is * a target and we need to re-authenticate */ node_printf(node, "Shutdown: explicit logo pend=%d ", !pend_frames_empty); node_printf(node, "nport.ini=%d node.tgt=%d\n", node->nport->enable_ini, node->targ); if (!pend_frames_empty || (node->nport->enable_ini && node->targ)) { u8 send_plogi = false; if (node->nport->enable_ini && node->targ) { /* * we're an initiator and * node shutting down is a target; * we'll need to re-authenticate in * initial state */ send_plogi = true; } /* * transition to __efc_d_init * (will retain HW node resources) */ node->els_io_enabled = true; node->req_free = false; /* * either pending frames exist or we are re-authenticating * with PLOGI (or both); in either case, return to initial * state */ efc_node_init_device(node, send_plogi); } /* else: let node shutdown occur */} static voidefc_node_purge_pending(struct efc_node *node){ struct efc *efc = node->efc; struct efc_hw_sequence *frame, *next; unsigned long flags = 0; spin_lock_irqsave(&node->pend_frames_lock, flags); list_for_each_entry_safe(frame, next, &node->pend_frames, list_entry) { list_del(&frame->list_entry); efc->tt.hw_seq_free(efc, frame); } spin_unlock_irqrestore(&node->pend_frames_lock, flags);} void__efc_node_shutdown(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg){ struct efc_node *node = ctx->app; efc_node_evt_set(ctx, evt, __func__); node_sm_trace(); switch (evt) { case EFC_EVT_ENTER: { efc_node_hold_frames(node); WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list)); /* by default, we will be freeing node after we unwind */ node->req_free = true; switch (node->shutdown_reason) { case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO: /* Node shutdown b/c of PLOGI received when node * already logged in. We have PLOGI service * parameters, so submit node attach; we won't be * freeing this node */ efc_node_handle_implicit_logo(node); break; case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO: efc_node_handle_explicit_logo(node); break; case EFC_NODE_SHUTDOWN_DEFAULT: default: { /* * shutdown due to link down, * node going away (xport event) or * nport shutdown, purge pending and * proceed to cleanup node */ /* cleanup any pending LS_ACC ELSs */ efc_node_send_ls_io_cleanup(node); node_printf(node, "Shutdown reason: default, purge pending\n"); efc_node_purge_pending(node); break; } } break; } case EFC_EVT_EXIT: efc_node_accept_frames(node); break; default: __efc_node_common(__func__, ctx, evt, arg); }} static boolefc_node_check_els_quiesced(struct efc_node *node){ /* check to see if ELS requests, completions are quiesced */ if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 && efc_els_io_list_empty(node, &node->els_ios_list)) { if (!node->attached) { /* hw node detach already completed, proceed */ node_printf(node, "HW node not attached\n"); efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL); } else { /* * hw node detach hasn't completed, * transition and wait */ node_printf(node, "HW node still attached\n"); efc_node_transition(node, __efc_node_wait_node_free, NULL); } return true; } return false;} voidefc_node_initiate_cleanup(struct efc_node *node){ /* * if ELS's have already been quiesced, will move to next state * if ELS's have not been quiesced, abort them */ if (!efc_node_check_els_quiesced(node)) { efc_node_hold_frames(node); efc_node_transition(node, __efc_node_wait_els_shutdown, NULL); }} void__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg){ bool check_quiesce = false; struct efc_node *node = ctx->app; efc_node_evt_set(ctx, evt, __func__); node_sm_trace(); /* Node state machine: Wait for all ELSs to complete */ switch (evt) { case EFC_EVT_ENTER: efc_node_hold_frames(node); if (efc_els_io_list_empty(node, &node->els_ios_list)) { node_printf(node, "All ELS IOs complete\n"); check_quiesce = true; } break; case EFC_EVT_EXIT: efc_node_accept_frames(node); break; case EFC_EVT_SRRS_ELS_REQ_OK: case EFC_EVT_SRRS_ELS_REQ_FAIL: case EFC_EVT_SRRS_ELS_REQ_RJT: case EFC_EVT_ELS_REQ_ABORTED: if (WARN_ON(!node->els_req_cnt)) break; node->els_req_cnt--; check_quiesce = true; break; case EFC_EVT_SRRS_ELS_CMPL_OK: case EFC_EVT_SRRS_ELS_CMPL_FAIL: if (WARN_ON(!node->els_cmpl_cnt)) break; node->els_cmpl_cnt--;Lines 1–500 of 1102. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021 Broadcom. All Rights Reserved. The term * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. */ #include "efc.h" int efc_remote_node_cb(void *arg, int event, void *data) { struct efc *efc = arg; struct efc_remote_node *rnode = data; struct efc_node *node = rnode->node; unsigned long flags = 0; spin_lock_irqsave(&efc->lock, flags); efc_node_post_event(node, event, NULL); spin_unlock_irqrestore(&efc->lock, flags); return 0; } struct efc_node * efc_node_find(struct efc_nport *nport, u32 port_id) { /* Find an FC node structure given the FC port ID */ return xa_load(&nport->lookup, port_id); } static void _efc_node_free(struct kref *arg) { struct efc_node *node = container_of(arg, struct efc_node, ref); struct efc *efc = node->efc; struct efc_dma *dma; dma = &node->sparm_dma_buf; dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); memset(dma, 0, sizeof(struct efc_dma)); mempool_free(node, efc->node_pool); } struct efc_node *efc_node_alloc(struct efc_nport *nport, u32 port_id, bool init, bool targ) { int rc; struct efc_node *node = NULL; struct efc *efc = nport->efc; struct efc_dma *dma; if (nport->shutting_down) { efc_log_debug(efc, "node allocation when shutting down %06x", port_id); return NULL; } node = mempool_alloc(efc->node_pool, GFP_ATOMIC); if (!node) { efc_log_err(efc, "node allocation failed %06x", port_id); return NULL; } memset(node, 0, sizeof(*node)); dma = &node->sparm_dma_buf; dma->size = NODE_SPARAMS_SIZE; dma->virt = dma_pool_zalloc(efc->node_dma_pool, GFP_ATOMIC, &dma->phys); if (!dma->virt) { efc_log_err(efc, "node dma alloc failed\n"); goto dma_fail; } node->rnode.indicator = U32_MAX; node->nport = nport; node->efc = efc; node->init = init; node->targ = targ; spin_lock_init(&node->pend_frames_lock); INIT_LIST_HEAD(&node->pend_frames); spin_lock_init(&node->els_ios_lock); INIT_LIST_HEAD(&node->els_ios_list); node->els_io_enabled = true; rc = efc_cmd_node_alloc(efc, &node->rnode, port_id, nport); if (rc) { efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc); goto hw_alloc_fail; } node->rnode.node = node; node->sm.app = node; node->evtdepth = 0; efc_node_update_display_name(node); rc = xa_err(xa_store(&nport->lookup, port_id, node, GFP_ATOMIC)); if (rc) { efc_log_err(efc, "Node lookup store failed: %d\n", rc); goto xa_fail; } /* initialize refcount */ kref_init(&node->ref); node->release = _efc_node_free; kref_get(&nport->ref); return node; xa_fail: efc_node_free_resources(efc, &node->rnode); hw_alloc_fail: dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); dma_fail: mempool_free(node, efc->node_pool); return NULL; } void efc_node_free(struct efc_node *node) { struct efc_nport *nport; struct efc *efc; int rc = 0; struct efc_node *ns = NULL; nport = node->nport; efc = node->efc; node_printf(node, "Free'd\n"); if (node->refound) { /* * Save the name server node. We will send fake RSCN event at * the end to handle ignored RSCN event during node deletion */ ns = efc_node_find(node->nport, FC_FID_DIR_SERV); } if (!node->nport) { efc_log_err(efc, "Node already Freed\n"); return; } /* Free HW resources */ rc = efc_node_free_resources(efc, &node->rnode); if (rc < 0) efc_log_err(efc, "efc_hw_node_free failed: %d\n", rc); /* if the gidpt_delay_timer is still running, then delete it */ if (timer_pending(&node->gidpt_delay_timer)) timer_delete(&node->gidpt_delay_timer); xa_erase(&nport->lookup, node->rnode.fc_id); /* * If the node_list is empty, * then post a ALL_CHILD_NODES_FREE event to the nport, * after the lock is released. * The nport may be free'd as a result of the event. */ if (xa_empty(&nport->lookup)) efc_sm_post_event(&nport->sm, EFC_EVT_ALL_CHILD_NODES_FREE, NULL); node->nport = NULL; node->sm.current_state = NULL; kref_put(&nport->ref, nport->release); kref_put(&node->ref, node->release); if (ns) { /* sending fake RSCN event to name server node */ efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL); } } static void efc_dma_copy_in(struct efc_dma *dma, void *buffer, u32 buffer_length) { if (!dma || !buffer || !buffer_length) return; if (buffer_length > dma->size) buffer_length = dma->size; memcpy(dma->virt, buffer, buffer_length); dma->len = buffer_length; } int efc_node_attach(struct efc_node *node) { int rc = 0; struct efc_nport *nport = node->nport; struct efc_domain *domain = nport->domain; struct efc *efc = node->efc; if (!domain->attached) { efc_log_err(efc, "Warning: unattached domain\n"); return -EIO; } /* Update node->wwpn/wwnn */ efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn), efc_node_get_wwpn(node)); efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn), efc_node_get_wwnn(node)); efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4, sizeof(node->service_params) - 4); /* take lock to protect node->rnode.attached */ rc = efc_cmd_node_attach(efc, &node->rnode, &node->sparm_dma_buf); if (rc < 0) efc_log_debug(efc, "efc_hw_node_attach failed: %d\n", rc); return rc; } void efc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length) { switch (fc_id) { case FC_FID_FLOGI: snprintf(buffer, buffer_length, "fabric"); break; case FC_FID_FCTRL: snprintf(buffer, buffer_length, "fabctl"); break; case FC_FID_DIR_SERV: snprintf(buffer, buffer_length, "nserve"); break; default: if (fc_id == FC_FID_DOM_MGR) { snprintf(buffer, buffer_length, "dctl%02x", (fc_id & 0x0000ff)); } else { snprintf(buffer, buffer_length, "%06x", fc_id); } break; } } void efc_node_update_display_name(struct efc_node *node) { u32 port_id = node->rnode.fc_id; struct efc_nport *nport = node->nport; char portid_display[16]; efc_node_fcid_display(port_id, portid_display, sizeof(portid_display)); snprintf(node->display_name, sizeof(node->display_name), "%s.%s", nport->display_name, portid_display); } void efc_node_send_ls_io_cleanup(struct efc_node *node) { if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) { efc_log_debug(node->efc, "[%s] cleaning up LS_ACC oxid=0x%x\n", node->display_name, node->ls_acc_oxid); node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; node->ls_acc_io = NULL; } } static void efc_node_handle_implicit_logo(struct efc_node *node) { int rc; /* * currently, only case for implicit logo is PLOGI * recvd. Thus, node's ELS IO pending list won't be * empty (PLOGI will be on it) */ WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI); node_printf(node, "Reason: implicit logout, re-authenticate\n"); /* Re-attach node with the same HW node resources */ node->req_free = false; rc = efc_node_attach(node); efc_node_transition(node, __efc_d_wait_node_attach, NULL); node->els_io_enabled = true; if (rc < 0) efc_node_post_event(node, EFC_EVT_NODE_ATTACH_FAIL, NULL); } static void efc_node_handle_explicit_logo(struct efc_node *node) { s8 pend_frames_empty; unsigned long flags = 0; /* cleanup any pending LS_ACC ELSs */ efc_node_send_ls_io_cleanup(node); spin_lock_irqsave(&node->pend_frames_lock, flags); pend_frames_empty = list_empty(&node->pend_frames); spin_unlock_irqrestore(&node->pend_frames_lock, flags); /* * there are two scenarios where we want to keep * this node alive: * 1. there are pending frames that need to be * processed or * 2. we're an initiator and the remote node is * a target and we need to re-authenticate */ node_printf(node, "Shutdown: explicit logo pend=%d ", !pend_frames_empty); node_printf(node, "nport.ini=%d node.tgt=%d\n", node->nport->enable_ini, node->targ); if (!pend_frames_empty || (node->nport->enable_ini && node->targ)) { u8 send_plogi = false; if (node->nport->enable_ini && node->targ) { /* * we're an initiator and * node shutting down is a target; * we'll need to re-authenticate in * initial state */ send_plogi = true; } /* * transition to __efc_d_init * (will retain HW node resources) */ node->els_io_enabled = true; node->req_free = false; /* * either pending frames exist or we are re-authenticating * with PLOGI (or both); in either case, return to initial * state */ efc_node_init_device(node, send_plogi); } /* else: let node shutdown occur */ } static void efc_node_purge_pending(struct efc_node *node) { struct efc *efc = node->efc; struct efc_hw_sequence *frame, *next; unsigned long flags = 0; spin_lock_irqsave(&node->pend_frames_lock, flags); list_for_each_entry_safe(frame, next, &node->pend_frames, list_entry) { list_del(&frame->list_entry); efc->tt.hw_seq_free(efc, frame); } spin_unlock_irqrestore(&node->pend_frames_lock, flags); } void __efc_node_shutdown(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg) { struct efc_node *node = ctx->app; efc_node_evt_set(ctx, evt, __func__); node_sm_trace(); switch (evt) { case EFC_EVT_ENTER: { efc_node_hold_frames(node); WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list)); /* by default, we will be freeing node after we unwind */ node->req_free = true; switch (node->shutdown_reason) { case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO: /* Node shutdown b/c of PLOGI received when node * already logged in. We have PLOGI service * parameters, so submit node attach; we won't be * freeing this node */ efc_node_handle_implicit_logo(node); break; case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO: efc_node_handle_explicit_logo(node); break; case EFC_NODE_SHUTDOWN_DEFAULT: default: { /* * shutdown due to link down, * node going away (xport event) or * nport shutdown, purge pending and * proceed to cleanup node */ /* cleanup any pending LS_ACC ELSs */ efc_node_send_ls_io_cleanup(node); node_printf(node, "Shutdown reason: default, purge pending\n"); efc_node_purge_pending(node); break; } } break; } case EFC_EVT_EXIT: efc_node_accept_frames(node); break; default: __efc_node_common(__func__, ctx, evt, arg); } } static bool efc_node_check_els_quiesced(struct efc_node *node) { /* check to see if ELS requests, completions are quiesced */ if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 && efc_els_io_list_empty(node, &node->els_ios_list)) { if (!node->attached) { /* hw node detach already completed, proceed */ node_printf(node, "HW node not attached\n"); efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL); } else { /* * hw node detach hasn't completed, * transition and wait */ node_printf(node, "HW node still attached\n"); efc_node_transition(node, __efc_node_wait_node_free, NULL); } return true; } return false; } void efc_node_initiate_cleanup(struct efc_node *node) { /* * if ELS's have already been quiesced, will move to next state * if ELS's have not been quiesced, abort them */ if (!efc_node_check_els_quiesced(node)) { efc_node_hold_frames(node); efc_node_transition(node, __efc_node_wait_els_shutdown, NULL); } } void __efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg) { bool check_quiesce = false; struct efc_node *node = ctx->app; efc_node_evt_set(ctx, evt, __func__); node_sm_trace(); /* Node state machine: Wait for all ELSs to complete */ switch (evt) { case EFC_EVT_ENTER: efc_node_hold_frames(node); if (efc_els_io_list_empty(node, &node->els_ios_list)) { node_printf(node, "All ELS IOs complete\n"); check_quiesce = true; } break; case EFC_EVT_EXIT: efc_node_accept_frames(node); break; case EFC_EVT_SRRS_ELS_REQ_OK: case EFC_EVT_SRRS_ELS_REQ_FAIL: case EFC_EVT_SRRS_ELS_REQ_RJT: case EFC_EVT_ELS_REQ_ABORTED: if (WARN_ON(!node->els_req_cnt)) break; node->els_req_cnt--; check_quiesce = true; break; case EFC_EVT_SRRS_ELS_CMPL_OK: case EFC_EVT_SRRS_ELS_CMPL_FAIL: if (WARN_ON(!node->els_cmpl_cnt)) break; node->els_cmpl_cnt--;