1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102 |
- // 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))
- del_timer(&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--;
- check_quiesce = true;
- break;
- case EFC_EVT_ALL_CHILD_NODES_FREE:
- /* all ELS IO's complete */
- node_printf(node, "All ELS IOs complete\n");
- WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list));
- check_quiesce = true;
- break;
- case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
- check_quiesce = true;
- break;
- case EFC_EVT_DOMAIN_ATTACH_OK:
- /* don't care about domain_attach_ok */
- break;
- /* ignore shutdown events as we're already in shutdown path */
- case EFC_EVT_SHUTDOWN:
- /* have default shutdown event take precedence */
- node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
- fallthrough;
- case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
- case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
- node_printf(node, "%s received\n", efc_sm_event_name(evt));
- break;
- default:
- __efc_node_common(__func__, ctx, evt, arg);
- }
- if (check_quiesce)
- efc_node_check_els_quiesced(node);
- }
- void
- __efc_node_wait_node_free(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);
- break;
- case EFC_EVT_EXIT:
- efc_node_accept_frames(node);
- break;
- case EFC_EVT_NODE_FREE_OK:
- /* node is officially no longer attached */
- node->attached = false;
- efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL);
- break;
- case EFC_EVT_ALL_CHILD_NODES_FREE:
- case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
- /* As IOs and ELS IO's complete we expect to get these events */
- break;
- case EFC_EVT_DOMAIN_ATTACH_OK:
- /* don't care about domain_attach_ok */
- break;
- /* ignore shutdown events as we're already in shutdown path */
- case EFC_EVT_SHUTDOWN:
- /* have default shutdown event take precedence */
- node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
- fallthrough;
- case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
- case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
- node_printf(node, "%s received\n", efc_sm_event_name(evt));
- break;
- default:
- __efc_node_common(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- struct efc_node *node = ctx->app;
- struct efc *efc = node->efc;
- efc_node_evt_set(ctx, evt, __func__);
- node_sm_trace();
- switch (evt) {
- case EFC_EVT_ENTER:
- efc_node_hold_frames(node);
- /* first check to see if no ELS IOs are outstanding */
- if (efc_els_io_list_empty(node, &node->els_ios_list))
- /* If there are any active IOS, Free them. */
- efc_node_transition(node, __efc_node_shutdown, NULL);
- break;
- case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
- case EFC_EVT_ALL_CHILD_NODES_FREE:
- if (efc_els_io_list_empty(node, &node->els_ios_list))
- efc_node_transition(node, __efc_node_shutdown, NULL);
- break;
- case EFC_EVT_EXIT:
- efc_node_accept_frames(node);
- break;
- case EFC_EVT_SRRS_ELS_REQ_FAIL:
- /* Can happen as ELS IO IO's complete */
- if (WARN_ON(!node->els_req_cnt))
- break;
- node->els_req_cnt--;
- break;
- /* ignore shutdown events as we're already in shutdown path */
- case EFC_EVT_SHUTDOWN:
- /* have default shutdown event take precedence */
- node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT;
- fallthrough;
- case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO:
- case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO:
- efc_log_debug(efc, "[%s] %-20s\n", node->display_name,
- efc_sm_event_name(evt));
- break;
- case EFC_EVT_DOMAIN_ATTACH_OK:
- /* don't care about domain_attach_ok */
- break;
- default:
- __efc_node_common(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_node_common(const char *funcname, struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- struct efc_node *node = NULL;
- struct efc *efc = NULL;
- struct efc_node_cb *cbdata = arg;
- node = ctx->app;
- efc = node->efc;
- switch (evt) {
- case EFC_EVT_ENTER:
- case EFC_EVT_REENTER:
- case EFC_EVT_EXIT:
- case EFC_EVT_NPORT_TOPOLOGY_NOTIFY:
- case EFC_EVT_NODE_MISSING:
- case EFC_EVT_FCP_CMD_RCVD:
- break;
- case EFC_EVT_NODE_REFOUND:
- node->refound = true;
- break;
- /*
- * node->attached must be set appropriately
- * for all node attach/detach events
- */
- case EFC_EVT_NODE_ATTACH_OK:
- node->attached = true;
- break;
- case EFC_EVT_NODE_FREE_OK:
- case EFC_EVT_NODE_ATTACH_FAIL:
- node->attached = false;
- break;
- /*
- * handle any ELS completions that
- * other states either didn't care about
- * or forgot about
- */
- 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--;
- break;
- /*
- * handle any ELS request completions that
- * other states either didn't care about
- * or forgot about
- */
- 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--;
- break;
- case EFC_EVT_ELS_RCVD: {
- struct fc_frame_header *hdr = cbdata->header->dma.virt;
- /*
- * Unsupported ELS was received,
- * send LS_RJT, command not supported
- */
- efc_log_debug(efc,
- "[%s] (%s) ELS x%02x, LS_RJT not supported\n",
- node->display_name, funcname,
- ((u8 *)cbdata->payload->dma.virt)[0]);
- efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
- ELS_RJT_UNSUP, ELS_EXPL_NONE, 0);
- break;
- }
- case EFC_EVT_PLOGI_RCVD:
- case EFC_EVT_FLOGI_RCVD:
- case EFC_EVT_LOGO_RCVD:
- case EFC_EVT_PRLI_RCVD:
- case EFC_EVT_PRLO_RCVD:
- case EFC_EVT_PDISC_RCVD:
- case EFC_EVT_FDISC_RCVD:
- case EFC_EVT_ADISC_RCVD:
- case EFC_EVT_RSCN_RCVD:
- case EFC_EVT_SCR_RCVD: {
- struct fc_frame_header *hdr = cbdata->header->dma.virt;
- /* sm: / send ELS_RJT */
- efc_log_debug(efc, "[%s] (%s) %s sending ELS_RJT\n",
- node->display_name, funcname,
- efc_sm_event_name(evt));
- /* if we didn't catch this in a state, send generic LS_RJT */
- efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id),
- ELS_RJT_UNAB, ELS_EXPL_NONE, 0);
- break;
- }
- case EFC_EVT_ABTS_RCVD: {
- efc_log_debug(efc, "[%s] (%s) %s sending BA_ACC\n",
- node->display_name, funcname,
- efc_sm_event_name(evt));
- /* sm: / send BA_ACC */
- efc_send_bls_acc(node, cbdata->header->dma.virt);
- break;
- }
- default:
- efc_log_debug(node->efc, "[%s] %-20s %-20s not handled\n",
- node->display_name, funcname,
- efc_sm_event_name(evt));
- }
- }
- void
- efc_node_save_sparms(struct efc_node *node, void *payload)
- {
- memcpy(node->service_params, payload, sizeof(node->service_params));
- }
- void
- efc_node_post_event(struct efc_node *node,
- enum efc_sm_event evt, void *arg)
- {
- bool free_node = false;
- node->evtdepth++;
- efc_sm_post_event(&node->sm, evt, arg);
- /* If our event call depth is one and
- * we're not holding frames
- * then we can dispatch any pending frames.
- * We don't want to allow the efc_process_node_pending()
- * call to recurse.
- */
- if (!node->hold_frames && node->evtdepth == 1)
- efc_process_node_pending(node);
- node->evtdepth--;
- /*
- * Free the node object if so requested,
- * and we're at an event call depth of zero
- */
- if (node->evtdepth == 0 && node->req_free)
- free_node = true;
- if (free_node)
- efc_node_free(node);
- }
- void
- efc_node_transition(struct efc_node *node,
- void (*state)(struct efc_sm_ctx *,
- enum efc_sm_event, void *), void *data)
- {
- struct efc_sm_ctx *ctx = &node->sm;
- if (ctx->current_state == state) {
- efc_node_post_event(node, EFC_EVT_REENTER, data);
- } else {
- efc_node_post_event(node, EFC_EVT_EXIT, data);
- ctx->current_state = state;
- efc_node_post_event(node, EFC_EVT_ENTER, data);
- }
- }
- void
- efc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name)
- {
- memset(buf, 0, buf_len);
- snprintf(buf, buf_len, "eui.%016llX", (unsigned long long)eui_name);
- }
- u64
- efc_node_get_wwpn(struct efc_node *node)
- {
- struct fc_els_flogi *sp =
- (struct fc_els_flogi *)node->service_params;
- return be64_to_cpu(sp->fl_wwpn);
- }
- u64
- efc_node_get_wwnn(struct efc_node *node)
- {
- struct fc_els_flogi *sp =
- (struct fc_els_flogi *)node->service_params;
- return be64_to_cpu(sp->fl_wwnn);
- }
- int
- efc_node_check_els_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg,
- u8 cmd, void (*efc_node_common_func)(const char *,
- struct efc_sm_ctx *, enum efc_sm_event, void *),
- const char *funcname)
- {
- return 0;
- }
- int
- efc_node_check_ns_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg,
- u16 cmd, void (*efc_node_common_func)(const char *,
- struct efc_sm_ctx *, enum efc_sm_event, void *),
- const char *funcname)
- {
- return 0;
- }
- int
- efc_els_io_list_empty(struct efc_node *node, struct list_head *list)
- {
- int empty;
- unsigned long flags = 0;
- spin_lock_irqsave(&node->els_ios_lock, flags);
- empty = list_empty(list);
- spin_unlock_irqrestore(&node->els_ios_lock, flags);
- return empty;
- }
- void
- efc_node_pause(struct efc_node *node,
- void (*state)(struct efc_sm_ctx *,
- enum efc_sm_event, void *))
- {
- node->nodedb_state = state;
- efc_node_transition(node, __efc_node_paused, NULL);
- }
- void
- __efc_node_paused(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();
- /*
- * This state is entered when a state is "paused". When resumed, the
- * node is transitioned to a previously saved state (node->ndoedb_state)
- */
- switch (evt) {
- case EFC_EVT_ENTER:
- node_printf(node, "Paused\n");
- break;
- case EFC_EVT_RESUME: {
- void (*pf)(struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg);
- pf = node->nodedb_state;
- node->nodedb_state = NULL;
- efc_node_transition(node, pf, NULL);
- break;
- }
- case EFC_EVT_DOMAIN_ATTACH_OK:
- break;
- case EFC_EVT_SHUTDOWN:
- node->req_free = true;
- break;
- default:
- __efc_node_common(__func__, ctx, evt, arg);
- }
- }
- void
- efc_node_recv_els_frame(struct efc_node *node,
- struct efc_hw_sequence *seq)
- {
- u32 prli_size = sizeof(struct fc_els_prli) + sizeof(struct fc_els_spp);
- struct {
- u32 cmd;
- enum efc_sm_event evt;
- u32 payload_size;
- } els_cmd_list[] = {
- {ELS_PLOGI, EFC_EVT_PLOGI_RCVD, sizeof(struct fc_els_flogi)},
- {ELS_FLOGI, EFC_EVT_FLOGI_RCVD, sizeof(struct fc_els_flogi)},
- {ELS_LOGO, EFC_EVT_LOGO_RCVD, sizeof(struct fc_els_ls_acc)},
- {ELS_PRLI, EFC_EVT_PRLI_RCVD, prli_size},
- {ELS_PRLO, EFC_EVT_PRLO_RCVD, prli_size},
- {ELS_PDISC, EFC_EVT_PDISC_RCVD, MAX_ACC_REJECT_PAYLOAD},
- {ELS_FDISC, EFC_EVT_FDISC_RCVD, MAX_ACC_REJECT_PAYLOAD},
- {ELS_ADISC, EFC_EVT_ADISC_RCVD, sizeof(struct fc_els_adisc)},
- {ELS_RSCN, EFC_EVT_RSCN_RCVD, MAX_ACC_REJECT_PAYLOAD},
- {ELS_SCR, EFC_EVT_SCR_RCVD, MAX_ACC_REJECT_PAYLOAD},
- };
- struct efc_node_cb cbdata;
- u8 *buf = seq->payload->dma.virt;
- enum efc_sm_event evt = EFC_EVT_ELS_RCVD;
- u32 i;
- memset(&cbdata, 0, sizeof(cbdata));
- cbdata.header = seq->header;
- cbdata.payload = seq->payload;
- /* find a matching event for the ELS command */
- for (i = 0; i < ARRAY_SIZE(els_cmd_list); i++) {
- if (els_cmd_list[i].cmd == buf[0]) {
- evt = els_cmd_list[i].evt;
- break;
- }
- }
- efc_node_post_event(node, evt, &cbdata);
- }
- void
- efc_node_recv_ct_frame(struct efc_node *node,
- struct efc_hw_sequence *seq)
- {
- struct fc_ct_hdr *iu = seq->payload->dma.virt;
- struct fc_frame_header *hdr = seq->header->dma.virt;
- struct efc *efc = node->efc;
- u16 gscmd = be16_to_cpu(iu->ct_cmd);
- efc_log_err(efc, "[%s] Received cmd :%x sending CT_REJECT\n",
- node->display_name, gscmd);
- efc_send_ct_rsp(efc, node, be16_to_cpu(hdr->fh_ox_id), iu,
- FC_FS_RJT, FC_FS_RJT_UNSUP, 0);
- }
- void
- efc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq)
- {
- struct efc_node_cb cbdata;
- memset(&cbdata, 0, sizeof(cbdata));
- cbdata.header = seq->header;
- cbdata.payload = seq->payload;
- efc_node_post_event(node, EFC_EVT_FCP_CMD_RCVD, &cbdata);
- }
- void
- efc_process_node_pending(struct efc_node *node)
- {
- struct efc *efc = node->efc;
- struct efc_hw_sequence *seq = NULL;
- u32 pend_frames_processed = 0;
- unsigned long flags = 0;
- for (;;) {
- /* need to check for hold frames condition after each frame
- * processed because any given frame could cause a transition
- * to a state that holds frames
- */
- if (node->hold_frames)
- break;
- seq = NULL;
- /* Get next frame/sequence */
- spin_lock_irqsave(&node->pend_frames_lock, flags);
- if (!list_empty(&node->pend_frames)) {
- seq = list_first_entry(&node->pend_frames,
- struct efc_hw_sequence, list_entry);
- list_del(&seq->list_entry);
- }
- spin_unlock_irqrestore(&node->pend_frames_lock, flags);
- if (!seq) {
- pend_frames_processed = node->pend_frames_processed;
- node->pend_frames_processed = 0;
- break;
- }
- node->pend_frames_processed++;
- /* now dispatch frame(s) to dispatch function */
- efc_node_dispatch_frame(node, seq);
- efc->tt.hw_seq_free(efc, seq);
- }
- if (pend_frames_processed != 0)
- efc_log_debug(efc, "%u node frames held and processed\n",
- pend_frames_processed);
- }
- void
- efc_scsi_sess_reg_complete(struct efc_node *node, u32 status)
- {
- unsigned long flags = 0;
- enum efc_sm_event evt = EFC_EVT_NODE_SESS_REG_OK;
- struct efc *efc = node->efc;
- if (status)
- evt = EFC_EVT_NODE_SESS_REG_FAIL;
- spin_lock_irqsave(&efc->lock, flags);
- /* Notify the node to resume */
- efc_node_post_event(node, evt, NULL);
- spin_unlock_irqrestore(&efc->lock, flags);
- }
- void
- efc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node)
- {
- unsigned long flags = 0;
- spin_lock_irqsave(&efc->lock, flags);
- /* Notify the node to resume */
- efc_node_post_event(node, EFC_EVT_NODE_DEL_INI_COMPLETE, NULL);
- spin_unlock_irqrestore(&efc->lock, flags);
- }
- void
- efc_scsi_del_target_complete(struct efc *efc, struct efc_node *node)
- {
- unsigned long flags = 0;
- spin_lock_irqsave(&efc->lock, flags);
- /* Notify the node to resume */
- efc_node_post_event(node, EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL);
- spin_unlock_irqrestore(&efc->lock, flags);
- }
- void
- efc_scsi_io_list_empty(struct efc *efc, struct efc_node *node)
- {
- unsigned long flags = 0;
- spin_lock_irqsave(&efc->lock, flags);
- efc_node_post_event(node, EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL);
- spin_unlock_irqrestore(&efc->lock, flags);
- }
- void efc_node_post_els_resp(struct efc_node *node, u32 evt, void *arg)
- {
- struct efc *efc = node->efc;
- unsigned long flags = 0;
- spin_lock_irqsave(&efc->lock, flags);
- efc_node_post_event(node, evt, arg);
- spin_unlock_irqrestore(&efc->lock, flags);
- }
- void efc_node_post_shutdown(struct efc_node *node, void *arg)
- {
- unsigned long flags = 0;
- struct efc *efc = node->efc;
- spin_lock_irqsave(&efc->lock, flags);
- efc_node_post_event(node, EFC_EVT_SHUTDOWN, arg);
- spin_unlock_irqrestore(&efc->lock, flags);
- }
|