1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
4 * ���Broadcom��� refers to Broadcom Inc. and/or its subsidiaries.
5 */
6
7/*
8 * domain_sm Domain State Machine: States
9 */
10
11#include "efc.h"
12
13int
14efc_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 void
104_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
115void
116efc_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
158void
159efc_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 void
171__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 shutdown
184		 */
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 void
193__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->efc
228
229void
230__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 port
273		 * Note: drec->fc_id is ALPA from read_topology only if loop
274		 */
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 if
288		 * ALPA == 0 is present, it will occupy the first
289		 * 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
356void
357__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 names
384		 */
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 ID
395			 * and don't need fabric login.
396			 * Transition to the allocated state and
397			 * post an event to attach to
398			 * the domain. Note that this breaks the
399			 * normal action/transition
400			 * pattern here to avoid a race with the
401			 * 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
458void
459__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 to
514			 * wait state and send shutdown to each
515			 * nport
516			 */
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
543void
544__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 avoid
559		 * duplicate domain event post
560		 */
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 request
575		 * has completed
576		 * Note: nport will have already received notification
577		 * 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 to
607		 * complete, then handle domain lost
608		 */
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 from
615		 * the other FLOGI path, so drop this one
616		 */
617		break;
618
619	default:
620		__efc_domain_common(__func__, ctx, evt, arg);
621	}
622}
623
624void
625__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 state
644			 * and send shutdown to each nport
645			 */
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_ID
683		 * is the same as the one we're working with
684		 */
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
694void
695__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
723void
724__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 allocate
738			 * a new one with the same fcf_wwn
739			 * 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 domains
757			 * 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
773void
774__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 completion
783	 * 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 to
791			 * wait state and send shutdown to each nport
792			 */
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
823void
824__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
833void
834efc_domain_attach(struct efc_domain *domain, u32 s_id)
835{
836	__efc_domain_attach_internal(domain, s_id);
837}
838
839int
840efc_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 void
858efct_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 frame
867		 * processed because any given frame could cause a transition
868		 * to a state that holds frames
869		 */
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
904void
905efc_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 or
911	 * there's already frames on the pending list,
912	 * then add the new frame to pending list
913	 */
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 frame
930		 * was not handled - so cleanup
931		 */
932		if (efc_domain_dispatch_frame(domain, seq))
933			efc->tt.hw_seq_free(efc, seq);
934	}
935}
936
937int
938efc_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 and
985		 * there is no node context, then we can drop the frame
986		 */
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
1022void
1023efc_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