12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
- * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
- */
- /*
- * domain_sm Domain State Machine: States
- */
- #include "efc.h"
- int
- efc_domain_cb(void *arg, int event, void *data)
- {
- struct efc *efc = arg;
- struct efc_domain *domain = NULL;
- int rc = 0;
- unsigned long flags = 0;
- if (event != EFC_HW_DOMAIN_FOUND)
- domain = data;
- /* Accept domain callback events from the user driver */
- spin_lock_irqsave(&efc->lock, flags);
- switch (event) {
- case EFC_HW_DOMAIN_FOUND: {
- u64 fcf_wwn = 0;
- struct efc_domain_record *drec = data;
- /* extract the fcf_wwn */
- fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn));
- efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn);
- /* lookup domain, or allocate a new one */
- domain = efc->domain;
- if (!domain) {
- domain = efc_domain_alloc(efc, fcf_wwn);
- if (!domain) {
- efc_log_err(efc, "efc_domain_alloc() failed\n");
- rc = -1;
- break;
- }
- efc_sm_transition(&domain->drvsm, __efc_domain_init,
- NULL);
- }
- efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec);
- break;
- }
- case EFC_HW_DOMAIN_LOST:
- domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n");
- efc->hold_frames = true;
- efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL);
- break;
- case EFC_HW_DOMAIN_ALLOC_OK:
- domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n");
- efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL);
- break;
- case EFC_HW_DOMAIN_ALLOC_FAIL:
- domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n");
- efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL,
- NULL);
- break;
- case EFC_HW_DOMAIN_ATTACH_OK:
- domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n");
- efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL);
- break;
- case EFC_HW_DOMAIN_ATTACH_FAIL:
- domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n");
- efc_domain_post_event(domain,
- EFC_EVT_DOMAIN_ATTACH_FAIL, NULL);
- break;
- case EFC_HW_DOMAIN_FREE_OK:
- domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n");
- efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL);
- break;
- case EFC_HW_DOMAIN_FREE_FAIL:
- domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n");
- efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL);
- break;
- default:
- efc_log_warn(efc, "unsupported event %#x\n", event);
- }
- spin_unlock_irqrestore(&efc->lock, flags);
- if (efc->domain && domain->req_accept_frames) {
- domain->req_accept_frames = false;
- efc->hold_frames = false;
- }
- return rc;
- }
- static void
- _efc_domain_free(struct kref *arg)
- {
- struct efc_domain *domain = container_of(arg, struct efc_domain, ref);
- struct efc *efc = domain->efc;
- if (efc->domain_free_cb)
- (*efc->domain_free_cb)(efc, efc->domain_free_cb_arg);
- kfree(domain);
- }
- void
- efc_domain_free(struct efc_domain *domain)
- {
- struct efc *efc;
- efc = domain->efc;
- /* Hold frames to clear the domain pointer from the xport lookup */
- efc->hold_frames = false;
- efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn);
- xa_destroy(&domain->lookup);
- efc->domain = NULL;
- kref_put(&domain->ref, domain->release);
- }
- struct efc_domain *
- efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn)
- {
- struct efc_domain *domain;
- domain = kzalloc(sizeof(*domain), GFP_ATOMIC);
- if (!domain)
- return NULL;
- domain->efc = efc;
- domain->drvsm.app = domain;
- /* initialize refcount */
- kref_init(&domain->ref);
- domain->release = _efc_domain_free;
- xa_init(&domain->lookup);
- INIT_LIST_HEAD(&domain->nport_list);
- efc->domain = domain;
- domain->fcf_wwn = fcf_wwn;
- efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn);
- return domain;
- }
- void
- efc_register_domain_free_cb(struct efc *efc,
- void (*callback)(struct efc *efc, void *arg),
- void *arg)
- {
- /* Register a callback to be called when the domain is freed */
- efc->domain_free_cb = callback;
- efc->domain_free_cb_arg = arg;
- if (!efc->domain && callback)
- (*callback)(efc, arg);
- }
- static void
- __efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- struct efc_domain *domain = ctx->app;
- switch (evt) {
- case EFC_EVT_ENTER:
- case EFC_EVT_REENTER:
- case EFC_EVT_EXIT:
- case EFC_EVT_ALL_CHILD_NODES_FREE:
- /*
- * this can arise if an FLOGI fails on the NPORT,
- * and the NPORT is shutdown
- */
- break;
- default:
- efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
- funcname, efc_sm_event_name(evt));
- }
- }
- static void
- __efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- struct efc_domain *domain = ctx->app;
- switch (evt) {
- case EFC_EVT_ENTER:
- case EFC_EVT_REENTER:
- case EFC_EVT_EXIT:
- break;
- case EFC_EVT_DOMAIN_FOUND:
- /* save drec, mark domain_found_pending */
- memcpy(&domain->pending_drec, arg,
- sizeof(domain->pending_drec));
- domain->domain_found_pending = true;
- break;
- case EFC_EVT_DOMAIN_LOST:
- /* unmark domain_found_pending */
- domain->domain_found_pending = false;
- break;
- default:
- efc_log_warn(domain->efc, "%-20s %-20s not handled\n",
- funcname, efc_sm_event_name(evt));
- }
- }
- #define std_domain_state_decl(...)\
- struct efc_domain *domain = NULL;\
- struct efc *efc = NULL;\
- \
- WARN_ON(!ctx || !ctx->app);\
- domain = ctx->app;\
- WARN_ON(!domain->efc);\
- efc = domain->efc
- void
- __efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
- void *arg)
- {
- std_domain_state_decl();
- domain_sm_trace(domain);
- switch (evt) {
- case EFC_EVT_ENTER:
- domain->attached = false;
- break;
- case EFC_EVT_DOMAIN_FOUND: {
- u32 i;
- struct efc_domain_record *drec = arg;
- struct efc_nport *nport;
- u64 my_wwnn = efc->req_wwnn;
- u64 my_wwpn = efc->req_wwpn;
- __be64 bewwpn;
- if (my_wwpn == 0 || my_wwnn == 0) {
- efc_log_debug(efc, "using default hardware WWN config\n");
- my_wwpn = efc->def_wwpn;
- my_wwnn = efc->def_wwnn;
- }
- efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n",
- my_wwpn, my_wwnn);
- /* Allocate a nport and transition to __efc_nport_allocated */
- nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX,
- efc->enable_ini, efc->enable_tgt);
- if (!nport) {
- efc_log_err(efc, "efc_nport_alloc() failed\n");
- break;
- }
- efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL);
- bewwpn = cpu_to_be64(nport->wwpn);
- /* allocate struct efc_nport object for local port
- * Note: drec->fc_id is ALPA from read_topology only if loop
- */
- if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) {
- efc_log_err(efc, "Can't allocate port\n");
- efc_nport_free(nport);
- break;
- }
- domain->is_loop = drec->is_loop;
- /*
- * If the loop position map includes ALPA == 0,
- * then we are in a public loop (NL_PORT)
- * Note that the first element of the loopmap[]
- * contains the count of elements, and if
- * ALPA == 0 is present, it will occupy the first
- * location after the count.
- */
- domain->is_nlport = drec->map.loop[1] == 0x00;
- if (!domain->is_loop) {
- /* Initiate HW domain alloc */
- if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
- efc_log_err(efc,
- "Failed to initiate HW domain allocation\n");
- break;
- }
- efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
- break;
- }
- efc_log_debug(efc, "%s fc_id=%#x speed=%d\n",
- drec->is_loop ?
- (domain->is_nlport ?
- "public-loop" : "loop") : "other",
- drec->fc_id, drec->speed);
- nport->fc_id = drec->fc_id;
- nport->topology = EFC_NPORT_TOPO_FC_AL;
- snprintf(nport->display_name, sizeof(nport->display_name),
- "s%06x", drec->fc_id);
- if (efc->enable_ini) {
- u32 count = drec->map.loop[0];
- efc_log_debug(efc, "%d position map entries\n",
- count);
- for (i = 1; i <= count; i++) {
- if (drec->map.loop[i] != drec->fc_id) {
- struct efc_node *node;
- efc_log_debug(efc, "%#x -> %#x\n",
- drec->fc_id,
- drec->map.loop[i]);
- node = efc_node_alloc(nport,
- drec->map.loop[i],
- false, true);
- if (!node) {
- efc_log_err(efc,
- "efc_node_alloc() failed\n");
- break;
- }
- efc_node_transition(node,
- __efc_d_wait_loop,
- NULL);
- }
- }
- }
- /* Initiate HW domain alloc */
- if (efc_cmd_domain_alloc(efc, domain, drec->index)) {
- efc_log_err(efc,
- "Failed to initiate HW domain allocation\n");
- break;
- }
- efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);
- break;
- }
- default:
- __efc_domain_common(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_domain_wait_alloc(struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- std_domain_state_decl();
- domain_sm_trace(domain);
- switch (evt) {
- case EFC_EVT_DOMAIN_ALLOC_OK: {
- struct fc_els_flogi *sp;
- struct efc_nport *nport;
- nport = domain->nport;
- if (WARN_ON(!nport))
- return;
- sp = (struct fc_els_flogi *)nport->service_params;
- /* Save the domain service parameters */
- memcpy(domain->service_params + 4, domain->dma.virt,
- sizeof(struct fc_els_flogi) - 4);
- memcpy(nport->service_params + 4, domain->dma.virt,
- sizeof(struct fc_els_flogi) - 4);
- /*
- * Update the nport's service parameters,
- * user might have specified non-default names
- */
- sp->fl_wwpn = cpu_to_be64(nport->wwpn);
- sp->fl_wwnn = cpu_to_be64(nport->wwnn);
- /*
- * Take the loop topology path,
- * unless we are an NL_PORT (public loop)
- */
- if (domain->is_loop && !domain->is_nlport) {
- /*
- * For loop, we already have our FC ID
- * and don't need fabric login.
- * Transition to the allocated state and
- * post an event to attach to
- * the domain. Note that this breaks the
- * normal action/transition
- * pattern here to avoid a race with the
- * domain attach callback.
- */
- /* sm: is_loop / domain_attach */
- efc_sm_transition(ctx, __efc_domain_allocated, NULL);
- __efc_domain_attach_internal(domain, nport->fc_id);
- break;
- }
- {
- struct efc_node *node;
- /* alloc fabric node, send FLOGI */
- node = efc_node_find(nport, FC_FID_FLOGI);
- if (node) {
- efc_log_err(efc,
- "Fabric Controller node already exists\n");
- break;
- }
- node = efc_node_alloc(nport, FC_FID_FLOGI,
- false, false);
- if (!node) {
- efc_log_err(efc,
- "Error: efc_node_alloc() failed\n");
- } else {
- efc_node_transition(node,
- __efc_fabric_init, NULL);
- }
- /* Accept frames */
- domain->req_accept_frames = true;
- }
- /* sm: / start fabric logins */
- efc_sm_transition(ctx, __efc_domain_allocated, NULL);
- break;
- }
- case EFC_EVT_DOMAIN_ALLOC_FAIL:
- efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;",
- efc_sm_event_name(evt));
- efc_log_err(efc, "shutting down domain\n");
- domain->req_domain_free = true;
- break;
- case EFC_EVT_DOMAIN_FOUND:
- /* Should not happen */
- break;
- case EFC_EVT_DOMAIN_LOST:
- efc_log_debug(efc,
- "%s received while waiting for hw_domain_alloc()\n",
- efc_sm_event_name(evt));
- efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
- break;
- default:
- __efc_domain_common(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_domain_allocated(struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- std_domain_state_decl();
- domain_sm_trace(domain);
- switch (evt) {
- case EFC_EVT_DOMAIN_REQ_ATTACH: {
- int rc = 0;
- u32 fc_id;
- if (WARN_ON(!arg))
- return;
- fc_id = *((u32 *)arg);
- efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n",
- fc_id);
- /* Update nport lookup */
- rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport,
- GFP_ATOMIC));
- if (rc) {
- efc_log_err(efc, "Sport lookup store failed: %d\n", rc);
- return;
- }
- /* Update display name for the nport */
- efc_node_fcid_display(fc_id, domain->nport->display_name,
- sizeof(domain->nport->display_name));
- /* Issue domain attach call */
- rc = efc_cmd_domain_attach(efc, domain, fc_id);
- if (rc) {
- efc_log_err(efc, "efc_hw_domain_attach failed: %d\n",
- rc);
- return;
- }
- /* sm: / domain_attach */
- efc_sm_transition(ctx, __efc_domain_wait_attach, NULL);
- break;
- }
- case EFC_EVT_DOMAIN_FOUND:
- /* Should not happen */
- efc_log_err(efc, "%s: evt: %d should not happen\n",
- __func__, evt);
- break;
- case EFC_EVT_DOMAIN_LOST: {
- efc_log_debug(efc,
- "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n",
- efc_sm_event_name(evt));
- if (!list_empty(&domain->nport_list)) {
- /*
- * if there are nports, transition to
- * wait state and send shutdown to each
- * nport
- */
- struct efc_nport *nport = NULL, *nport_next = NULL;
- efc_sm_transition(ctx, __efc_domain_wait_nports_free,
- NULL);
- list_for_each_entry_safe(nport, nport_next,
- &domain->nport_list,
- list_entry) {
- efc_sm_post_event(&nport->sm,
- EFC_EVT_SHUTDOWN, NULL);
- }
- } else {
- /* no nports exist, free domain */
- efc_sm_transition(ctx, __efc_domain_wait_shutdown,
- NULL);
- if (efc_cmd_domain_free(efc, domain))
- efc_log_err(efc, "hw_domain_free failed\n");
- }
- break;
- }
- default:
- __efc_domain_common(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_domain_wait_attach(struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- std_domain_state_decl();
- domain_sm_trace(domain);
- switch (evt) {
- case EFC_EVT_DOMAIN_ATTACH_OK: {
- struct efc_node *node = NULL;
- struct efc_nport *nport, *next_nport;
- unsigned long index;
- /*
- * Set domain notify pending state to avoid
- * duplicate domain event post
- */
- domain->domain_notify_pend = true;
- /* Mark as attached */
- domain->attached = true;
- /* Transition to ready */
- /* sm: / forward event to all nports and nodes */
- efc_sm_transition(ctx, __efc_domain_ready, NULL);
- /* We have an FCFI, so we can accept frames */
- domain->req_accept_frames = true;
- /*
- * Notify all nodes that the domain attach request
- * has completed
- * Note: nport will have already received notification
- * of nport attached as a result of the HW's port attach.
- */
- list_for_each_entry_safe(nport, next_nport,
- &domain->nport_list, list_entry) {
- xa_for_each(&nport->lookup, index, node) {
- efc_node_post_event(node,
- EFC_EVT_DOMAIN_ATTACH_OK,
- NULL);
- }
- }
- domain->domain_notify_pend = false;
- break;
- }
- case EFC_EVT_DOMAIN_ATTACH_FAIL:
- efc_log_debug(efc,
- "%s received while waiting for hw attach\n",
- efc_sm_event_name(evt));
- break;
- case EFC_EVT_DOMAIN_FOUND:
- /* Should not happen */
- efc_log_err(efc, "%s: evt: %d should not happen\n",
- __func__, evt);
- break;
- case EFC_EVT_DOMAIN_LOST:
- /*
- * Domain lost while waiting for an attach to complete,
- * go to a state that waits for the domain attach to
- * complete, then handle domain lost
- */
- efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);
- break;
- case EFC_EVT_DOMAIN_REQ_ATTACH:
- /*
- * In P2P we can get an attach request from
- * the other FLOGI path, so drop this one
- */
- break;
- default:
- __efc_domain_common(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)
- {
- std_domain_state_decl();
- domain_sm_trace(domain);
- switch (evt) {
- case EFC_EVT_ENTER: {
- /* start any pending vports */
- if (efc_vport_start(domain)) {
- efc_log_debug(domain->efc,
- "efc_vport_start didn't start vports\n");
- }
- break;
- }
- case EFC_EVT_DOMAIN_LOST: {
- if (!list_empty(&domain->nport_list)) {
- /*
- * if there are nports, transition to wait state
- * and send shutdown to each nport
- */
- struct efc_nport *nport = NULL, *nport_next = NULL;
- efc_sm_transition(ctx, __efc_domain_wait_nports_free,
- NULL);
- list_for_each_entry_safe(nport, nport_next,
- &domain->nport_list,
- list_entry) {
- efc_sm_post_event(&nport->sm,
- EFC_EVT_SHUTDOWN, NULL);
- }
- } else {
- /* no nports exist, free domain */
- efc_sm_transition(ctx, __efc_domain_wait_shutdown,
- NULL);
- if (efc_cmd_domain_free(efc, domain))
- efc_log_err(efc, "hw_domain_free failed\n");
- }
- break;
- }
- case EFC_EVT_DOMAIN_FOUND:
- /* Should not happen */
- efc_log_err(efc, "%s: evt: %d should not happen\n",
- __func__, evt);
- break;
- case EFC_EVT_DOMAIN_REQ_ATTACH: {
- /* can happen during p2p */
- u32 fc_id;
- fc_id = *((u32 *)arg);
- /* Assume that the domain is attached */
- WARN_ON(!domain->attached);
- /*
- * Verify that the requested FC_ID
- * is the same as the one we're working with
- */
- WARN_ON(domain->nport->fc_id != fc_id);
- break;
- }
- default:
- __efc_domain_common(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt,
- void *arg)
- {
- std_domain_state_decl();
- domain_sm_trace(domain);
- /* Wait for nodes to free prior to the domain shutdown */
- switch (evt) {
- case EFC_EVT_ALL_CHILD_NODES_FREE: {
- int rc;
- /* sm: / efc_hw_domain_free */
- efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL);
- /* Request efc_hw_domain_free and wait for completion */
- rc = efc_cmd_domain_free(efc, domain);
- if (rc) {
- efc_log_err(efc, "efc_hw_domain_free() failed: %d\n",
- rc);
- }
- break;
- }
- default:
- __efc_domain_common_shutdown(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_domain_wait_shutdown(struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- std_domain_state_decl();
- domain_sm_trace(domain);
- switch (evt) {
- case EFC_EVT_DOMAIN_FREE_OK:
- /* sm: / domain_free */
- if (domain->domain_found_pending) {
- /*
- * save fcf_wwn and drec from this domain,
- * free current domain and allocate
- * a new one with the same fcf_wwn
- * could use a SLI-4 "re-register VPI"
- * operation here?
- */
- u64 fcf_wwn = domain->fcf_wwn;
- struct efc_domain_record drec = domain->pending_drec;
- efc_log_debug(efc, "Reallocating domain\n");
- domain->req_domain_free = true;
- domain = efc_domain_alloc(efc, fcf_wwn);
- if (!domain) {
- efc_log_err(efc,
- "efc_domain_alloc() failed\n");
- return;
- }
- /*
- * got a new domain; at this point,
- * there are at least two domains
- * once the req_domain_free flag is processed,
- * the associated domain will be removed.
- */
- efc_sm_transition(&domain->drvsm, __efc_domain_init,
- NULL);
- efc_sm_post_event(&domain->drvsm,
- EFC_EVT_DOMAIN_FOUND, &drec);
- } else {
- domain->req_domain_free = true;
- }
- break;
- default:
- __efc_domain_common_shutdown(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx,
- enum efc_sm_event evt, void *arg)
- {
- std_domain_state_decl();
- domain_sm_trace(domain);
- /*
- * Wait for the domain alloc/attach completion
- * after receiving a domain lost.
- */
- switch (evt) {
- case EFC_EVT_DOMAIN_ALLOC_OK:
- case EFC_EVT_DOMAIN_ATTACH_OK: {
- if (!list_empty(&domain->nport_list)) {
- /*
- * if there are nports, transition to
- * wait state and send shutdown to each nport
- */
- struct efc_nport *nport = NULL, *nport_next = NULL;
- efc_sm_transition(ctx, __efc_domain_wait_nports_free,
- NULL);
- list_for_each_entry_safe(nport, nport_next,
- &domain->nport_list,
- list_entry) {
- efc_sm_post_event(&nport->sm,
- EFC_EVT_SHUTDOWN, NULL);
- }
- } else {
- /* no nports exist, free domain */
- efc_sm_transition(ctx, __efc_domain_wait_shutdown,
- NULL);
- if (efc_cmd_domain_free(efc, domain))
- efc_log_err(efc, "hw_domain_free() failed\n");
- }
- break;
- }
- case EFC_EVT_DOMAIN_ALLOC_FAIL:
- case EFC_EVT_DOMAIN_ATTACH_FAIL:
- efc_log_err(efc, "[domain] %-20s: failed\n",
- efc_sm_event_name(evt));
- break;
- default:
- __efc_domain_common_shutdown(__func__, ctx, evt, arg);
- }
- }
- void
- __efc_domain_attach_internal(struct efc_domain *domain, u32 s_id)
- {
- memcpy(domain->dma.virt,
- ((uint8_t *)domain->flogi_service_params) + 4,
- sizeof(struct fc_els_flogi) - 4);
- (void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH,
- &s_id);
- }
- void
- efc_domain_attach(struct efc_domain *domain, u32 s_id)
- {
- __efc_domain_attach_internal(domain, s_id);
- }
- int
- efc_domain_post_event(struct efc_domain *domain,
- enum efc_sm_event event, void *arg)
- {
- int rc;
- bool req_domain_free;
- rc = efc_sm_post_event(&domain->drvsm, event, arg);
- req_domain_free = domain->req_domain_free;
- domain->req_domain_free = false;
- if (req_domain_free)
- efc_domain_free(domain);
- return rc;
- }
- static void
- efct_domain_process_pending(struct efc_domain *domain)
- {
- struct efc *efc = domain->efc;
- struct efc_hw_sequence *seq = NULL;
- u32 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 (efc->hold_frames)
- break;
- /* Get next frame/sequence */
- spin_lock_irqsave(&efc->pend_frames_lock, flags);
- if (!list_empty(&efc->pend_frames)) {
- seq = list_first_entry(&efc->pend_frames,
- struct efc_hw_sequence, list_entry);
- list_del(&seq->list_entry);
- }
- if (!seq) {
- processed = efc->pend_frames_processed;
- efc->pend_frames_processed = 0;
- spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
- break;
- }
- efc->pend_frames_processed++;
- spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
- /* now dispatch frame(s) to dispatch function */
- if (efc_domain_dispatch_frame(domain, seq))
- efc->tt.hw_seq_free(efc, seq);
- seq = NULL;
- }
- if (processed != 0)
- efc_log_debug(efc, "%u domain frames held and processed\n",
- processed);
- }
- void
- efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq)
- {
- struct efc_domain *domain = efc->domain;
- /*
- * If we are holding frames or the domain is not yet registered or
- * there's already frames on the pending list,
- * then add the new frame to pending list
- */
- if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) {
- unsigned long flags = 0;
- spin_lock_irqsave(&efc->pend_frames_lock, flags);
- INIT_LIST_HEAD(&seq->list_entry);
- list_add_tail(&seq->list_entry, &efc->pend_frames);
- spin_unlock_irqrestore(&efc->pend_frames_lock, flags);
- if (domain) {
- /* immediately process pending frames */
- efct_domain_process_pending(domain);
- }
- } else {
- /*
- * We are not holding frames and pending list is empty,
- * just process frame. A non-zero return means the frame
- * was not handled - so cleanup
- */
- if (efc_domain_dispatch_frame(domain, seq))
- efc->tt.hw_seq_free(efc, seq);
- }
- }
- int
- efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
- {
- struct efc_domain *domain = (struct efc_domain *)arg;
- struct efc *efc = domain->efc;
- struct fc_frame_header *hdr;
- struct efc_node *node = NULL;
- struct efc_nport *nport = NULL;
- unsigned long flags = 0;
- u32 s_id, d_id, rc = EFC_HW_SEQ_FREE;
- if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) {
- efc_log_err(efc, "Sequence header or payload is null\n");
- return rc;
- }
- hdr = seq->header->dma.virt;
- /* extract the s_id and d_id */
- s_id = ntoh24(hdr->fh_s_id);
- d_id = ntoh24(hdr->fh_d_id);
- spin_lock_irqsave(&efc->lock, flags);
- nport = efc_nport_find(domain, d_id);
- if (!nport) {
- if (hdr->fh_type == FC_TYPE_FCP) {
- /* Drop frame */
- efc_log_warn(efc, "FCP frame with invalid d_id x%x\n",
- d_id);
- goto out;
- }
- /* p2p will use this case */
- nport = domain->nport;
- if (!nport || !kref_get_unless_zero(&nport->ref)) {
- efc_log_err(efc, "Physical nport is NULL\n");
- goto out;
- }
- }
- /* Lookup the node given the remote s_id */
- node = efc_node_find(nport, s_id);
- /* If not found, then create a new node */
- if (!node) {
- /*
- * If this is solicited data or control based on R_CTL and
- * there is no node context, then we can drop the frame
- */
- if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) ||
- (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) {
- efc_log_debug(efc, "sol data/ctrl frame without node\n");
- goto out_release;
- }
- node = efc_node_alloc(nport, s_id, false, false);
- if (!node) {
- efc_log_err(efc, "efc_node_alloc() failed\n");
- goto out_release;
- }
- /* don't send PLOGI on efc_d_init entry */
- efc_node_init_device(node, false);
- }
- if (node->hold_frames || !list_empty(&node->pend_frames)) {
- /* add frame to node's pending list */
- spin_lock(&node->pend_frames_lock);
- INIT_LIST_HEAD(&seq->list_entry);
- list_add_tail(&seq->list_entry, &node->pend_frames);
- spin_unlock(&node->pend_frames_lock);
- rc = EFC_HW_SEQ_HOLD;
- goto out_release;
- }
- /* now dispatch frame to the node frame handler */
- efc_node_dispatch_frame(node, seq);
- out_release:
- kref_put(&nport->ref, nport->release);
- out:
- spin_unlock_irqrestore(&efc->lock, flags);
- return rc;
- }
- void
- efc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq)
- {
- struct fc_frame_header *hdr = seq->header->dma.virt;
- u32 port_id;
- struct efc_node *node = (struct efc_node *)arg;
- struct efc *efc = node->efc;
- port_id = ntoh24(hdr->fh_s_id);
- if (WARN_ON(port_id != node->rnode.fc_id))
- return;
- if ((!(ntoh24(hdr->fh_f_ctl) & FC_FC_END_SEQ)) ||
- !(ntoh24(hdr->fh_f_ctl) & FC_FC_SEQ_INIT)) {
- node_printf(node,
- "Drop frame hdr = %08x %08x %08x %08x %08x %08x\n",
- cpu_to_be32(((u32 *)hdr)[0]),
- cpu_to_be32(((u32 *)hdr)[1]),
- cpu_to_be32(((u32 *)hdr)[2]),
- cpu_to_be32(((u32 *)hdr)[3]),
- cpu_to_be32(((u32 *)hdr)[4]),
- cpu_to_be32(((u32 *)hdr)[5]));
- return;
- }
- switch (hdr->fh_r_ctl) {
- case FC_RCTL_ELS_REQ:
- case FC_RCTL_ELS_REP:
- efc_node_recv_els_frame(node, seq);
- break;
- case FC_RCTL_BA_ABTS:
- case FC_RCTL_BA_ACC:
- case FC_RCTL_BA_RJT:
- case FC_RCTL_BA_NOP:
- efc_log_err(efc, "Received ABTS:\n");
- break;
- case FC_RCTL_DD_UNSOL_CMD:
- case FC_RCTL_DD_UNSOL_CTL:
- switch (hdr->fh_type) {
- case FC_TYPE_FCP:
- if ((hdr->fh_r_ctl & 0xf) == FC_RCTL_DD_UNSOL_CMD) {
- if (!node->fcp_enabled) {
- efc_node_recv_fcp_cmd(node, seq);
- break;
- }
- efc_log_err(efc, "Recvd FCP CMD. Drop IO\n");
- } else if ((hdr->fh_r_ctl & 0xf) ==
- FC_RCTL_DD_SOL_DATA) {
- node_printf(node,
- "solicited data recvd. Drop IO\n");
- }
- break;
- case FC_TYPE_CT:
- efc_node_recv_ct_frame(node, seq);
- break;
- default:
- break;
- }
- break;
- default:
- efc_log_err(efc, "Unhandled frame rctl: %02x\n", hdr->fh_r_ctl);
- }
- }
|