brintos

brintos / linux-shallow public Read only

0
0
Text · 25.9 KiB · ca9d7ff Raw
1089 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2021 Broadcom. All Rights Reserved. The term4 * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.5 */6 7/*8 * domain_sm Domain State Machine: States9 */10 11#include "efc.h"12 13int14efc_domain_cb(void *arg, int event, void *data)15{16	struct efc *efc = arg;17	struct efc_domain *domain = NULL;18	int rc = 0;19	unsigned long flags = 0;20 21	if (event != EFC_HW_DOMAIN_FOUND)22		domain = data;23 24	/* Accept domain callback events from the user driver */25	spin_lock_irqsave(&efc->lock, flags);26	switch (event) {27	case EFC_HW_DOMAIN_FOUND: {28		u64 fcf_wwn = 0;29		struct efc_domain_record *drec = data;30 31		/* extract the fcf_wwn */32		fcf_wwn = be64_to_cpu(*((__be64 *)drec->wwn));33 34		efc_log_debug(efc, "Domain found: wwn %016llX\n", fcf_wwn);35 36		/* lookup domain, or allocate a new one */37		domain = efc->domain;38		if (!domain) {39			domain = efc_domain_alloc(efc, fcf_wwn);40			if (!domain) {41				efc_log_err(efc, "efc_domain_alloc() failed\n");42				rc = -1;43				break;44			}45			efc_sm_transition(&domain->drvsm, __efc_domain_init,46					  NULL);47		}48		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FOUND, drec);49		break;50	}51 52	case EFC_HW_DOMAIN_LOST:53		domain_trace(domain, "EFC_HW_DOMAIN_LOST:\n");54		efc->hold_frames = true;55		efc_domain_post_event(domain, EFC_EVT_DOMAIN_LOST, NULL);56		break;57 58	case EFC_HW_DOMAIN_ALLOC_OK:59		domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_OK:\n");60		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_OK, NULL);61		break;62 63	case EFC_HW_DOMAIN_ALLOC_FAIL:64		domain_trace(domain, "EFC_HW_DOMAIN_ALLOC_FAIL:\n");65		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ALLOC_FAIL,66				      NULL);67		break;68 69	case EFC_HW_DOMAIN_ATTACH_OK:70		domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_OK:\n");71		efc_domain_post_event(domain, EFC_EVT_DOMAIN_ATTACH_OK, NULL);72		break;73 74	case EFC_HW_DOMAIN_ATTACH_FAIL:75		domain_trace(domain, "EFC_HW_DOMAIN_ATTACH_FAIL:\n");76		efc_domain_post_event(domain,77				      EFC_EVT_DOMAIN_ATTACH_FAIL, NULL);78		break;79 80	case EFC_HW_DOMAIN_FREE_OK:81		domain_trace(domain, "EFC_HW_DOMAIN_FREE_OK:\n");82		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_OK, NULL);83		break;84 85	case EFC_HW_DOMAIN_FREE_FAIL:86		domain_trace(domain, "EFC_HW_DOMAIN_FREE_FAIL:\n");87		efc_domain_post_event(domain, EFC_EVT_DOMAIN_FREE_FAIL, NULL);88		break;89 90	default:91		efc_log_warn(efc, "unsupported event %#x\n", event);92	}93	spin_unlock_irqrestore(&efc->lock, flags);94 95	if (efc->domain && domain->req_accept_frames) {96		domain->req_accept_frames = false;97		efc->hold_frames = false;98	}99 100	return rc;101}102 103static void104_efc_domain_free(struct kref *arg)105{106	struct efc_domain *domain = container_of(arg, struct efc_domain, ref);107	struct efc *efc = domain->efc;108 109	if (efc->domain_free_cb)110		(*efc->domain_free_cb)(efc, efc->domain_free_cb_arg);111 112	kfree(domain);113}114 115void116efc_domain_free(struct efc_domain *domain)117{118	struct efc *efc;119 120	efc = domain->efc;121 122	/* Hold frames to clear the domain pointer from the xport lookup */123	efc->hold_frames = false;124 125	efc_log_debug(efc, "Domain free: wwn %016llX\n", domain->fcf_wwn);126 127	xa_destroy(&domain->lookup);128	efc->domain = NULL;129	kref_put(&domain->ref, domain->release);130}131 132struct efc_domain *133efc_domain_alloc(struct efc *efc, uint64_t fcf_wwn)134{135	struct efc_domain *domain;136 137	domain = kzalloc(sizeof(*domain), GFP_ATOMIC);138	if (!domain)139		return NULL;140 141	domain->efc = efc;142	domain->drvsm.app = domain;143 144	/* initialize refcount */145	kref_init(&domain->ref);146	domain->release = _efc_domain_free;147 148	xa_init(&domain->lookup);149 150	INIT_LIST_HEAD(&domain->nport_list);151	efc->domain = domain;152	domain->fcf_wwn = fcf_wwn;153	efc_log_debug(efc, "Domain allocated: wwn %016llX\n", domain->fcf_wwn);154 155	return domain;156}157 158void159efc_register_domain_free_cb(struct efc *efc,160			    void (*callback)(struct efc *efc, void *arg),161			    void *arg)162{163	/* Register a callback to be called when the domain is freed */164	efc->domain_free_cb = callback;165	efc->domain_free_cb_arg = arg;166	if (!efc->domain && callback)167		(*callback)(efc, arg);168}169 170static void171__efc_domain_common(const char *funcname, struct efc_sm_ctx *ctx,172		    enum efc_sm_event evt, void *arg)173{174	struct efc_domain *domain = ctx->app;175 176	switch (evt) {177	case EFC_EVT_ENTER:178	case EFC_EVT_REENTER:179	case EFC_EVT_EXIT:180	case EFC_EVT_ALL_CHILD_NODES_FREE:181		/*182		 * this can arise if an FLOGI fails on the NPORT,183		 * and the NPORT is shutdown184		 */185		break;186	default:187		efc_log_warn(domain->efc, "%-20s %-20s not handled\n",188			     funcname, efc_sm_event_name(evt));189	}190}191 192static void193__efc_domain_common_shutdown(const char *funcname, struct efc_sm_ctx *ctx,194			     enum efc_sm_event evt, void *arg)195{196	struct efc_domain *domain = ctx->app;197 198	switch (evt) {199	case EFC_EVT_ENTER:200	case EFC_EVT_REENTER:201	case EFC_EVT_EXIT:202		break;203	case EFC_EVT_DOMAIN_FOUND:204		/* save drec, mark domain_found_pending */205		memcpy(&domain->pending_drec, arg,206		       sizeof(domain->pending_drec));207		domain->domain_found_pending = true;208		break;209	case EFC_EVT_DOMAIN_LOST:210		/* unmark domain_found_pending */211		domain->domain_found_pending = false;212		break;213 214	default:215		efc_log_warn(domain->efc, "%-20s %-20s not handled\n",216			     funcname, efc_sm_event_name(evt));217	}218}219 220#define std_domain_state_decl(...)\221	struct efc_domain *domain = NULL;\222	struct efc *efc = NULL;\223	\224	WARN_ON(!ctx || !ctx->app);\225	domain = ctx->app;\226	WARN_ON(!domain->efc);\227	efc = domain->efc228 229void230__efc_domain_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt,231		  void *arg)232{233	std_domain_state_decl();234 235	domain_sm_trace(domain);236 237	switch (evt) {238	case EFC_EVT_ENTER:239		domain->attached = false;240		break;241 242	case EFC_EVT_DOMAIN_FOUND: {243		u32	i;244		struct efc_domain_record *drec = arg;245		struct efc_nport *nport;246 247		u64 my_wwnn = efc->req_wwnn;248		u64 my_wwpn = efc->req_wwpn;249		__be64 bewwpn;250 251		if (my_wwpn == 0 || my_wwnn == 0) {252			efc_log_debug(efc, "using default hardware WWN config\n");253			my_wwpn = efc->def_wwpn;254			my_wwnn = efc->def_wwnn;255		}256 257		efc_log_debug(efc, "Create nport WWPN %016llX WWNN %016llX\n",258			      my_wwpn, my_wwnn);259 260		/* Allocate a nport and transition to __efc_nport_allocated */261		nport = efc_nport_alloc(domain, my_wwpn, my_wwnn, U32_MAX,262					efc->enable_ini, efc->enable_tgt);263 264		if (!nport) {265			efc_log_err(efc, "efc_nport_alloc() failed\n");266			break;267		}268		efc_sm_transition(&nport->sm, __efc_nport_allocated, NULL);269 270		bewwpn = cpu_to_be64(nport->wwpn);271 272		/* allocate struct efc_nport object for local port273		 * Note: drec->fc_id is ALPA from read_topology only if loop274		 */275		if (efc_cmd_nport_alloc(efc, nport, NULL, (uint8_t *)&bewwpn)) {276			efc_log_err(efc, "Can't allocate port\n");277			efc_nport_free(nport);278			break;279		}280 281		domain->is_loop = drec->is_loop;282 283		/*284		 * If the loop position map includes ALPA == 0,285		 * then we are in a public loop (NL_PORT)286		 * Note that the first element of the loopmap[]287		 * contains the count of elements, and if288		 * ALPA == 0 is present, it will occupy the first289		 * location after the count.290		 */291		domain->is_nlport = drec->map.loop[1] == 0x00;292 293		if (!domain->is_loop) {294			/* Initiate HW domain alloc */295			if (efc_cmd_domain_alloc(efc, domain, drec->index)) {296				efc_log_err(efc,297					    "Failed to initiate HW domain allocation\n");298				break;299			}300			efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);301			break;302		}303 304		efc_log_debug(efc, "%s fc_id=%#x speed=%d\n",305			      drec->is_loop ?306			      (domain->is_nlport ?307			      "public-loop" : "loop") : "other",308			      drec->fc_id, drec->speed);309 310		nport->fc_id = drec->fc_id;311		nport->topology = EFC_NPORT_TOPO_FC_AL;312		snprintf(nport->display_name, sizeof(nport->display_name),313			 "s%06x", drec->fc_id);314 315		if (efc->enable_ini) {316			u32 count = drec->map.loop[0];317 318			efc_log_debug(efc, "%d position map entries\n",319				      count);320			for (i = 1; i <= count; i++) {321				if (drec->map.loop[i] != drec->fc_id) {322					struct efc_node *node;323 324					efc_log_debug(efc, "%#x -> %#x\n",325						      drec->fc_id,326						      drec->map.loop[i]);327					node = efc_node_alloc(nport,328							      drec->map.loop[i],329							      false, true);330					if (!node) {331						efc_log_err(efc,332							    "efc_node_alloc() failed\n");333						break;334					}335					efc_node_transition(node,336							    __efc_d_wait_loop,337							    NULL);338				}339			}340		}341 342		/* Initiate HW domain alloc */343		if (efc_cmd_domain_alloc(efc, domain, drec->index)) {344			efc_log_err(efc,345				    "Failed to initiate HW domain allocation\n");346			break;347		}348		efc_sm_transition(ctx, __efc_domain_wait_alloc, arg);349		break;350	}351	default:352		__efc_domain_common(__func__, ctx, evt, arg);353	}354}355 356void357__efc_domain_wait_alloc(struct efc_sm_ctx *ctx,358			enum efc_sm_event evt, void *arg)359{360	std_domain_state_decl();361 362	domain_sm_trace(domain);363 364	switch (evt) {365	case EFC_EVT_DOMAIN_ALLOC_OK: {366		struct fc_els_flogi  *sp;367		struct efc_nport *nport;368 369		nport = domain->nport;370		if (WARN_ON(!nport))371			return;372 373		sp = (struct fc_els_flogi  *)nport->service_params;374 375		/* Save the domain service parameters */376		memcpy(domain->service_params + 4, domain->dma.virt,377		       sizeof(struct fc_els_flogi) - 4);378		memcpy(nport->service_params + 4, domain->dma.virt,379		       sizeof(struct fc_els_flogi) - 4);380 381		/*382		 * Update the nport's service parameters,383		 * user might have specified non-default names384		 */385		sp->fl_wwpn = cpu_to_be64(nport->wwpn);386		sp->fl_wwnn = cpu_to_be64(nport->wwnn);387 388		/*389		 * Take the loop topology path,390		 * unless we are an NL_PORT (public loop)391		 */392		if (domain->is_loop && !domain->is_nlport) {393			/*394			 * For loop, we already have our FC ID395			 * and don't need fabric login.396			 * Transition to the allocated state and397			 * post an event to attach to398			 * the domain. Note that this breaks the399			 * normal action/transition400			 * pattern here to avoid a race with the401			 * domain attach callback.402			 */403			/* sm: is_loop / domain_attach */404			efc_sm_transition(ctx, __efc_domain_allocated, NULL);405			__efc_domain_attach_internal(domain, nport->fc_id);406			break;407		}408		{409			struct efc_node *node;410 411			/* alloc fabric node, send FLOGI */412			node = efc_node_find(nport, FC_FID_FLOGI);413			if (node) {414				efc_log_err(efc,415					    "Fabric Controller node already exists\n");416				break;417			}418			node = efc_node_alloc(nport, FC_FID_FLOGI,419					      false, false);420			if (!node) {421				efc_log_err(efc,422					    "Error: efc_node_alloc() failed\n");423			} else {424				efc_node_transition(node,425						    __efc_fabric_init, NULL);426			}427			/* Accept frames */428			domain->req_accept_frames = true;429		}430		/* sm: / start fabric logins */431		efc_sm_transition(ctx, __efc_domain_allocated, NULL);432		break;433	}434 435	case EFC_EVT_DOMAIN_ALLOC_FAIL:436		efc_log_err(efc, "%s recv'd waiting for DOMAIN_ALLOC_OK;",437			    efc_sm_event_name(evt));438		efc_log_err(efc, "shutting down domain\n");439		domain->req_domain_free = true;440		break;441 442	case EFC_EVT_DOMAIN_FOUND:443		/* Should not happen */444		break;445 446	case EFC_EVT_DOMAIN_LOST:447		efc_log_debug(efc,448			      "%s received while waiting for hw_domain_alloc()\n",449			efc_sm_event_name(evt));450		efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);451		break;452 453	default:454		__efc_domain_common(__func__, ctx, evt, arg);455	}456}457 458void459__efc_domain_allocated(struct efc_sm_ctx *ctx,460		       enum efc_sm_event evt, void *arg)461{462	std_domain_state_decl();463 464	domain_sm_trace(domain);465 466	switch (evt) {467	case EFC_EVT_DOMAIN_REQ_ATTACH: {468		int rc = 0;469		u32 fc_id;470 471		if (WARN_ON(!arg))472			return;473 474		fc_id = *((u32 *)arg);475		efc_log_debug(efc, "Requesting hw domain attach fc_id x%x\n",476			      fc_id);477		/* Update nport lookup */478		rc = xa_err(xa_store(&domain->lookup, fc_id, domain->nport,479				     GFP_ATOMIC));480		if (rc) {481			efc_log_err(efc, "Sport lookup store failed: %d\n", rc);482			return;483		}484 485		/* Update display name for the nport */486		efc_node_fcid_display(fc_id, domain->nport->display_name,487				      sizeof(domain->nport->display_name));488 489		/* Issue domain attach call */490		rc = efc_cmd_domain_attach(efc, domain, fc_id);491		if (rc) {492			efc_log_err(efc, "efc_hw_domain_attach failed: %d\n",493				    rc);494			return;495		}496		/* sm: / domain_attach */497		efc_sm_transition(ctx, __efc_domain_wait_attach, NULL);498		break;499	}500 501	case EFC_EVT_DOMAIN_FOUND:502		/* Should not happen */503		efc_log_err(efc, "%s: evt: %d should not happen\n",504			    __func__, evt);505		break;506 507	case EFC_EVT_DOMAIN_LOST: {508		efc_log_debug(efc,509			      "%s received while in EFC_EVT_DOMAIN_REQ_ATTACH\n",510			efc_sm_event_name(evt));511		if (!list_empty(&domain->nport_list)) {512			/*513			 * if there are nports, transition to514			 * wait state and send shutdown to each515			 * nport516			 */517			struct efc_nport *nport = NULL, *nport_next = NULL;518 519			efc_sm_transition(ctx, __efc_domain_wait_nports_free,520					  NULL);521			list_for_each_entry_safe(nport, nport_next,522						 &domain->nport_list,523						 list_entry) {524				efc_sm_post_event(&nport->sm,525						  EFC_EVT_SHUTDOWN, NULL);526			}527		} else {528			/* no nports exist, free domain */529			efc_sm_transition(ctx, __efc_domain_wait_shutdown,530					  NULL);531			if (efc_cmd_domain_free(efc, domain))532				efc_log_err(efc, "hw_domain_free failed\n");533		}534 535		break;536	}537 538	default:539		__efc_domain_common(__func__, ctx, evt, arg);540	}541}542 543void544__efc_domain_wait_attach(struct efc_sm_ctx *ctx,545			 enum efc_sm_event evt, void *arg)546{547	std_domain_state_decl();548 549	domain_sm_trace(domain);550 551	switch (evt) {552	case EFC_EVT_DOMAIN_ATTACH_OK: {553		struct efc_node *node = NULL;554		struct efc_nport *nport, *next_nport;555		unsigned long index;556 557		/*558		 * Set domain notify pending state to avoid559		 * duplicate domain event post560		 */561		domain->domain_notify_pend = true;562 563		/* Mark as attached */564		domain->attached = true;565 566		/* Transition to ready */567		/* sm: / forward event to all nports and nodes */568		efc_sm_transition(ctx, __efc_domain_ready, NULL);569 570		/* We have an FCFI, so we can accept frames */571		domain->req_accept_frames = true;572 573		/*574		 * Notify all nodes that the domain attach request575		 * has completed576		 * Note: nport will have already received notification577		 * of nport attached as a result of the HW's port attach.578		 */579		list_for_each_entry_safe(nport, next_nport,580					 &domain->nport_list, list_entry) {581			xa_for_each(&nport->lookup, index, node) {582				efc_node_post_event(node,583						    EFC_EVT_DOMAIN_ATTACH_OK,584						    NULL);585			}586		}587		domain->domain_notify_pend = false;588		break;589	}590 591	case EFC_EVT_DOMAIN_ATTACH_FAIL:592		efc_log_debug(efc,593			      "%s received while waiting for hw attach\n",594			      efc_sm_event_name(evt));595		break;596 597	case EFC_EVT_DOMAIN_FOUND:598		/* Should not happen */599		efc_log_err(efc, "%s: evt: %d should not happen\n",600			    __func__, evt);601		break;602 603	case EFC_EVT_DOMAIN_LOST:604		/*605		 * Domain lost while waiting for an attach to complete,606		 * go to a state that waits for  the domain attach to607		 * complete, then handle domain lost608		 */609		efc_sm_transition(ctx, __efc_domain_wait_domain_lost, NULL);610		break;611 612	case EFC_EVT_DOMAIN_REQ_ATTACH:613		/*614		 * In P2P we can get an attach request from615		 * the other FLOGI path, so drop this one616		 */617		break;618 619	default:620		__efc_domain_common(__func__, ctx, evt, arg);621	}622}623 624void625__efc_domain_ready(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg)626{627	std_domain_state_decl();628 629	domain_sm_trace(domain);630 631	switch (evt) {632	case EFC_EVT_ENTER: {633		/* start any pending vports */634		if (efc_vport_start(domain)) {635			efc_log_debug(domain->efc,636				      "efc_vport_start didn't start vports\n");637		}638		break;639	}640	case EFC_EVT_DOMAIN_LOST: {641		if (!list_empty(&domain->nport_list)) {642			/*643			 * if there are nports, transition to wait state644			 * and send shutdown to each nport645			 */646			struct efc_nport *nport = NULL, *nport_next = NULL;647 648			efc_sm_transition(ctx, __efc_domain_wait_nports_free,649					  NULL);650			list_for_each_entry_safe(nport, nport_next,651						 &domain->nport_list,652						 list_entry) {653				efc_sm_post_event(&nport->sm,654						  EFC_EVT_SHUTDOWN, NULL);655			}656		} else {657			/* no nports exist, free domain */658			efc_sm_transition(ctx, __efc_domain_wait_shutdown,659					  NULL);660			if (efc_cmd_domain_free(efc, domain))661				efc_log_err(efc, "hw_domain_free failed\n");662		}663		break;664	}665 666	case EFC_EVT_DOMAIN_FOUND:667		/* Should not happen */668		efc_log_err(efc, "%s: evt: %d should not happen\n",669			    __func__, evt);670		break;671 672	case EFC_EVT_DOMAIN_REQ_ATTACH: {673		/* can happen during p2p */674		u32 fc_id;675 676		fc_id = *((u32 *)arg);677 678		/* Assume that the domain is attached */679		WARN_ON(!domain->attached);680 681		/*682		 * Verify that the requested FC_ID683		 * is the same as the one we're working with684		 */685		WARN_ON(domain->nport->fc_id != fc_id);686		break;687	}688 689	default:690		__efc_domain_common(__func__, ctx, evt, arg);691	}692}693 694void695__efc_domain_wait_nports_free(struct efc_sm_ctx *ctx, enum efc_sm_event evt,696			      void *arg)697{698	std_domain_state_decl();699 700	domain_sm_trace(domain);701 702	/* Wait for nodes to free prior to the domain shutdown */703	switch (evt) {704	case EFC_EVT_ALL_CHILD_NODES_FREE: {705		int rc;706 707		/* sm: / efc_hw_domain_free */708		efc_sm_transition(ctx, __efc_domain_wait_shutdown, NULL);709 710		/* Request efc_hw_domain_free and wait for completion */711		rc = efc_cmd_domain_free(efc, domain);712		if (rc) {713			efc_log_err(efc, "efc_hw_domain_free() failed: %d\n",714				    rc);715		}716		break;717	}718	default:719		__efc_domain_common_shutdown(__func__, ctx, evt, arg);720	}721}722 723void724__efc_domain_wait_shutdown(struct efc_sm_ctx *ctx,725			   enum efc_sm_event evt, void *arg)726{727	std_domain_state_decl();728 729	domain_sm_trace(domain);730 731	switch (evt) {732	case EFC_EVT_DOMAIN_FREE_OK:733		/* sm: / domain_free */734		if (domain->domain_found_pending) {735			/*736			 * save fcf_wwn and drec from this domain,737			 * free current domain and allocate738			 * a new one with the same fcf_wwn739			 * could use a SLI-4 "re-register VPI"740			 * operation here?741			 */742			u64 fcf_wwn = domain->fcf_wwn;743			struct efc_domain_record drec = domain->pending_drec;744 745			efc_log_debug(efc, "Reallocating domain\n");746			domain->req_domain_free = true;747			domain = efc_domain_alloc(efc, fcf_wwn);748 749			if (!domain) {750				efc_log_err(efc,751					    "efc_domain_alloc() failed\n");752				return;753			}754			/*755			 * got a new domain; at this point,756			 * there are at least two domains757			 * once the req_domain_free flag is processed,758			 * the associated domain will be removed.759			 */760			efc_sm_transition(&domain->drvsm, __efc_domain_init,761					  NULL);762			efc_sm_post_event(&domain->drvsm,763					  EFC_EVT_DOMAIN_FOUND, &drec);764		} else {765			domain->req_domain_free = true;766		}767		break;768	default:769		__efc_domain_common_shutdown(__func__, ctx, evt, arg);770	}771}772 773void774__efc_domain_wait_domain_lost(struct efc_sm_ctx *ctx,775			      enum efc_sm_event evt, void *arg)776{777	std_domain_state_decl();778 779	domain_sm_trace(domain);780 781	/*782	 * Wait for the domain alloc/attach completion783	 * after receiving a domain lost.784	 */785	switch (evt) {786	case EFC_EVT_DOMAIN_ALLOC_OK:787	case EFC_EVT_DOMAIN_ATTACH_OK: {788		if (!list_empty(&domain->nport_list)) {789			/*790			 * if there are nports, transition to791			 * wait state and send shutdown to each nport792			 */793			struct efc_nport *nport = NULL, *nport_next = NULL;794 795			efc_sm_transition(ctx, __efc_domain_wait_nports_free,796					  NULL);797			list_for_each_entry_safe(nport, nport_next,798						 &domain->nport_list,799						 list_entry) {800				efc_sm_post_event(&nport->sm,801						  EFC_EVT_SHUTDOWN, NULL);802			}803		} else {804			/* no nports exist, free domain */805			efc_sm_transition(ctx, __efc_domain_wait_shutdown,806					  NULL);807			if (efc_cmd_domain_free(efc, domain))808				efc_log_err(efc, "hw_domain_free() failed\n");809		}810		break;811	}812	case EFC_EVT_DOMAIN_ALLOC_FAIL:813	case EFC_EVT_DOMAIN_ATTACH_FAIL:814		efc_log_err(efc, "[domain] %-20s: failed\n",815			    efc_sm_event_name(evt));816		break;817 818	default:819		__efc_domain_common_shutdown(__func__, ctx, evt, arg);820	}821}822 823void824__efc_domain_attach_internal(struct efc_domain *domain, u32 s_id)825{826	memcpy(domain->dma.virt,827	       ((uint8_t *)domain->flogi_service_params) + 4,828		   sizeof(struct fc_els_flogi) - 4);829	(void)efc_sm_post_event(&domain->drvsm, EFC_EVT_DOMAIN_REQ_ATTACH,830				 &s_id);831}832 833void834efc_domain_attach(struct efc_domain *domain, u32 s_id)835{836	__efc_domain_attach_internal(domain, s_id);837}838 839int840efc_domain_post_event(struct efc_domain *domain,841		      enum efc_sm_event event, void *arg)842{843	int rc;844	bool req_domain_free;845 846	rc = efc_sm_post_event(&domain->drvsm, event, arg);847 848	req_domain_free = domain->req_domain_free;849	domain->req_domain_free = false;850 851	if (req_domain_free)852		efc_domain_free(domain);853 854	return rc;855}856 857static void858efct_domain_process_pending(struct efc_domain *domain)859{860	struct efc *efc = domain->efc;861	struct efc_hw_sequence *seq = NULL;862	u32 processed = 0;863	unsigned long flags = 0;864 865	for (;;) {866		/* need to check for hold frames condition after each frame867		 * processed because any given frame could cause a transition868		 * to a state that holds frames869		 */870		if (efc->hold_frames)871			break;872 873		/* Get next frame/sequence */874		spin_lock_irqsave(&efc->pend_frames_lock, flags);875 876		if (!list_empty(&efc->pend_frames)) {877			seq = list_first_entry(&efc->pend_frames,878					struct efc_hw_sequence, list_entry);879			list_del(&seq->list_entry);880		}881 882		if (!seq) {883			processed = efc->pend_frames_processed;884			efc->pend_frames_processed = 0;885			spin_unlock_irqrestore(&efc->pend_frames_lock, flags);886			break;887		}888		efc->pend_frames_processed++;889 890		spin_unlock_irqrestore(&efc->pend_frames_lock, flags);891 892		/* now dispatch frame(s) to dispatch function */893		if (efc_domain_dispatch_frame(domain, seq))894			efc->tt.hw_seq_free(efc, seq);895 896		seq = NULL;897	}898 899	if (processed != 0)900		efc_log_debug(efc, "%u domain frames held and processed\n",901			      processed);902}903 904void905efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq)906{907	struct efc_domain *domain = efc->domain;908 909	/*910	 * If we are holding frames or the domain is not yet registered or911	 * there's already frames on the pending list,912	 * then add the new frame to pending list913	 */914	if (!domain || efc->hold_frames || !list_empty(&efc->pend_frames)) {915		unsigned long flags = 0;916 917		spin_lock_irqsave(&efc->pend_frames_lock, flags);918		INIT_LIST_HEAD(&seq->list_entry);919		list_add_tail(&seq->list_entry, &efc->pend_frames);920		spin_unlock_irqrestore(&efc->pend_frames_lock, flags);921 922		if (domain) {923			/* immediately process pending frames */924			efct_domain_process_pending(domain);925		}926	} else {927		/*928		 * We are not holding frames and pending list is empty,929		 * just process frame. A non-zero return means the frame930		 * was not handled - so cleanup931		 */932		if (efc_domain_dispatch_frame(domain, seq))933			efc->tt.hw_seq_free(efc, seq);934	}935}936 937int938efc_domain_dispatch_frame(void *arg, struct efc_hw_sequence *seq)939{940	struct efc_domain *domain = (struct efc_domain *)arg;941	struct efc *efc = domain->efc;942	struct fc_frame_header *hdr;943	struct efc_node *node = NULL;944	struct efc_nport *nport = NULL;945	unsigned long flags = 0;946	u32 s_id, d_id, rc = EFC_HW_SEQ_FREE;947 948	if (!seq->header || !seq->header->dma.virt || !seq->payload->dma.virt) {949		efc_log_err(efc, "Sequence header or payload is null\n");950		return rc;951	}952 953	hdr = seq->header->dma.virt;954 955	/* extract the s_id and d_id */956	s_id = ntoh24(hdr->fh_s_id);957	d_id = ntoh24(hdr->fh_d_id);958 959	spin_lock_irqsave(&efc->lock, flags);960 961	nport = efc_nport_find(domain, d_id);962	if (!nport) {963		if (hdr->fh_type == FC_TYPE_FCP) {964			/* Drop frame */965			efc_log_warn(efc, "FCP frame with invalid d_id x%x\n",966				     d_id);967			goto out;968		}969 970		/* p2p will use this case */971		nport = domain->nport;972		if (!nport || !kref_get_unless_zero(&nport->ref)) {973			efc_log_err(efc, "Physical nport is NULL\n");974			goto out;975		}976	}977 978	/* Lookup the node given the remote s_id */979	node = efc_node_find(nport, s_id);980 981	/* If not found, then create a new node */982	if (!node) {983		/*984		 * If this is solicited data or control based on R_CTL and985		 * there is no node context, then we can drop the frame986		 */987		if ((hdr->fh_r_ctl == FC_RCTL_DD_SOL_DATA) ||988		    (hdr->fh_r_ctl == FC_RCTL_DD_SOL_CTL)) {989			efc_log_debug(efc, "sol data/ctrl frame without node\n");990			goto out_release;991		}992 993		node = efc_node_alloc(nport, s_id, false, false);994		if (!node) {995			efc_log_err(efc, "efc_node_alloc() failed\n");996			goto out_release;997		}998		/* don't send PLOGI on efc_d_init entry */999		efc_node_init_device(node, false);1000	}1001 1002	if (node->hold_frames || !list_empty(&node->pend_frames)) {1003		/* add frame to node's pending list */1004		spin_lock(&node->pend_frames_lock);1005		INIT_LIST_HEAD(&seq->list_entry);1006		list_add_tail(&seq->list_entry, &node->pend_frames);1007		spin_unlock(&node->pend_frames_lock);1008		rc = EFC_HW_SEQ_HOLD;1009		goto out_release;1010	}1011 1012	/* now dispatch frame to the node frame handler */1013	efc_node_dispatch_frame(node, seq);1014 1015out_release:1016	kref_put(&nport->ref, nport->release);1017out:1018	spin_unlock_irqrestore(&efc->lock, flags);1019	return rc;1020}1021 1022void1023efc_node_dispatch_frame(void *arg, struct efc_hw_sequence *seq)1024{1025	struct fc_frame_header *hdr = seq->header->dma.virt;1026	u32 port_id;1027	struct efc_node *node = (struct efc_node *)arg;1028	struct efc *efc = node->efc;1029 1030	port_id = ntoh24(hdr->fh_s_id);1031 1032	if (WARN_ON(port_id != node->rnode.fc_id))1033		return;1034 1035	if ((!(ntoh24(hdr->fh_f_ctl) & FC_FC_END_SEQ)) ||1036	    !(ntoh24(hdr->fh_f_ctl) & FC_FC_SEQ_INIT)) {1037		node_printf(node,1038			    "Drop frame hdr = %08x %08x %08x %08x %08x %08x\n",1039			    cpu_to_be32(((u32 *)hdr)[0]),1040			    cpu_to_be32(((u32 *)hdr)[1]),1041			    cpu_to_be32(((u32 *)hdr)[2]),1042			    cpu_to_be32(((u32 *)hdr)[3]),1043			    cpu_to_be32(((u32 *)hdr)[4]),1044			    cpu_to_be32(((u32 *)hdr)[5]));1045		return;1046	}1047 1048	switch (hdr->fh_r_ctl) {1049	case FC_RCTL_ELS_REQ:1050	case FC_RCTL_ELS_REP:1051		efc_node_recv_els_frame(node, seq);1052		break;1053 1054	case FC_RCTL_BA_ABTS:1055	case FC_RCTL_BA_ACC:1056	case FC_RCTL_BA_RJT:1057	case FC_RCTL_BA_NOP:1058		efc_log_err(efc, "Received ABTS:\n");1059		break;1060 1061	case FC_RCTL_DD_UNSOL_CMD:1062	case FC_RCTL_DD_UNSOL_CTL:1063		switch (hdr->fh_type) {1064		case FC_TYPE_FCP:1065			if ((hdr->fh_r_ctl & 0xf) == FC_RCTL_DD_UNSOL_CMD) {1066				if (!node->fcp_enabled) {1067					efc_node_recv_fcp_cmd(node, seq);1068					break;1069				}1070				efc_log_err(efc, "Recvd FCP CMD. Drop IO\n");1071			} else if ((hdr->fh_r_ctl & 0xf) ==1072							FC_RCTL_DD_SOL_DATA) {1073				node_printf(node,1074					    "solicited data recvd. Drop IO\n");1075			}1076			break;1077 1078		case FC_TYPE_CT:1079			efc_node_recv_ct_frame(node, seq);1080			break;1081		default:1082			break;1083		}1084		break;1085	default:1086		efc_log_err(efc, "Unhandled frame rctl: %02x\n", hdr->fh_r_ctl);1087	}1088}1089