xref: /freebsd/sys/dev/ocs_fc/ocs_domain.c (revision 4d846d26)
1 /*-
2  * Copyright (c) 2017 Broadcom. All rights reserved.
3  * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 /**
35  * @file
36  * Handles the domain object callback from the HW.
37  */
38 
39 /*!
40 @defgroup domain_sm Domain State Machine: States
41 */
42 
43 #include "ocs.h"
44 
45 #include "ocs_fabric.h"
46 #include "ocs_device.h"
47 
48 #define domain_sm_trace(domain)  \
49 	do { \
50 		if (OCS_LOG_ENABLE_DOMAIN_SM_TRACE(domain->ocs)) \
51 			ocs_log_info(domain->ocs, "[domain] %-20s %-20s\n", __func__, ocs_sm_event_name(evt)); \
52 	} while (0)
53 
54 #define domain_trace(domain, fmt, ...) \
55 	do { \
56 		if (OCS_LOG_ENABLE_DOMAIN_SM_TRACE(domain ? domain->ocs : NULL)) \
57 			ocs_log_info(domain ? domain->ocs : NULL, fmt, ##__VA_ARGS__); \
58 	} while (0)
59 
60 #define domain_printf(domain, fmt, ...) \
61 	do { \
62 		ocs_log_info(domain ? domain->ocs : NULL, fmt, ##__VA_ARGS__); \
63 	} while (0)
64 
65 void ocs_mgmt_domain_list(ocs_textbuf_t *textbuf, void *domain);
66 void ocs_mgmt_domain_get_all(ocs_textbuf_t *textbuf, void *domain);
67 int ocs_mgmt_domain_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *domain);
68 int ocs_mgmt_domain_set(char *parent, char *name, char *value, void *domain);
69 int ocs_mgmt_domain_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length,
70 		void *arg_out, uint32_t arg_out_length, void *domain);
71 
72 static ocs_mgmt_functions_t domain_mgmt_functions = {
73 	.get_list_handler = ocs_mgmt_domain_list,
74 	.get_handler = ocs_mgmt_domain_get,
75 	.get_all_handler = ocs_mgmt_domain_get_all,
76 	.set_handler = ocs_mgmt_domain_set,
77 	.exec_handler = ocs_mgmt_domain_exec,
78 };
79 
80 /**
81  * @brief Accept domain callback events from the HW.
82  *
83  * <h3 class="desc">Description</h3>
84  * HW calls this function with various domain-related events.
85  *
86  * @param arg Application-specified argument.
87  * @param event Domain event.
88  * @param data Event specific data.
89  *
90  * @return Returns 0 on success; or a negative error value on failure.
91  */
92 
93 int32_t
94 ocs_domain_cb(void *arg, ocs_hw_domain_event_e event, void *data)
95 {
96 	ocs_t *ocs = arg;
97 	ocs_domain_t *domain = NULL;
98 	int32_t rc = 0;
99 
100 	ocs_assert(data, -1);
101 
102 	if (event != OCS_HW_DOMAIN_FOUND) {
103 		domain = data;
104 	}
105 
106 	switch (event) {
107 	case OCS_HW_DOMAIN_FOUND: {
108 		uint64_t fcf_wwn = 0;
109 		ocs_domain_record_t *drec = data;
110 		ocs_assert(drec, -1);
111 
112 		/* extract the fcf_wwn */
113 		fcf_wwn = ocs_be64toh(*((uint64_t*)drec->wwn));
114 
115 		/* lookup domain, or allocate a new one if one doesn't exist already */
116 		domain = ocs_domain_find(ocs, fcf_wwn);
117 		if (domain == NULL) {
118 			domain = ocs_domain_alloc(ocs, fcf_wwn);
119 			if (domain == NULL) {
120 				ocs_log_err(ocs, "ocs_domain_alloc() failed\n");
121 				rc = -1;
122 				break;
123 			}
124 			ocs_sm_transition(&domain->drvsm, __ocs_domain_init, NULL);
125 		}
126 		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FOUND, drec);
127 		break;
128 	}
129 
130 	case OCS_HW_DOMAIN_LOST:
131 		domain_trace(domain, "OCS_HW_DOMAIN_LOST:\n");
132 		ocs_domain_hold_frames(domain);
133 		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_LOST, NULL);
134 		break;
135 
136 	case OCS_HW_DOMAIN_ALLOC_OK: {
137 		domain_trace(domain, "OCS_HW_DOMAIN_ALLOC_OK:\n");
138 		domain->instance_index = 0;
139 		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ALLOC_OK, NULL);
140 		break;
141 	}
142 
143 	case OCS_HW_DOMAIN_ALLOC_FAIL:
144 		domain_trace(domain, "OCS_HW_DOMAIN_ALLOC_FAIL:\n");
145 		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ALLOC_FAIL, NULL);
146 		break;
147 
148 	case OCS_HW_DOMAIN_ATTACH_OK:
149 		domain_trace(domain, "OCS_HW_DOMAIN_ATTACH_OK:\n");
150 		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ATTACH_OK, NULL);
151 		break;
152 
153 	case OCS_HW_DOMAIN_ATTACH_FAIL:
154 		domain_trace(domain, "OCS_HW_DOMAIN_ATTACH_FAIL:\n");
155 		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ATTACH_FAIL, NULL);
156 		break;
157 
158 	case OCS_HW_DOMAIN_FREE_OK:
159 		domain_trace(domain, "OCS_HW_DOMAIN_FREE_OK:\n");
160 		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FREE_OK, NULL);
161 		break;
162 
163 	case OCS_HW_DOMAIN_FREE_FAIL:
164 		domain_trace(domain, "OCS_HW_DOMAIN_FREE_FAIL:\n");
165 		ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FREE_FAIL, NULL);
166 		break;
167 
168 	default:
169 		ocs_log_warn(ocs, "unsupported event %#x\n", event);
170 	}
171 
172 	return rc;
173 }
174 
175 /**
176  * @brief Find the domain, given its FCF_WWN.
177  *
178  * <h3 class="desc">Description</h3>
179  * Search the domain_list to find a matching domain object.
180  *
181  * @param ocs Pointer to the OCS device.
182  * @param fcf_wwn FCF WWN to find.
183  *
184  * @return Returns the pointer to the domain if found; or NULL otherwise.
185  */
186 
187 ocs_domain_t *
188 ocs_domain_find(ocs_t *ocs, uint64_t fcf_wwn)
189 {
190 	ocs_domain_t *domain = NULL;
191 
192 	/* Check to see if this domain is already allocated */
193 	ocs_device_lock(ocs);
194 		ocs_list_foreach(&ocs->domain_list, domain) {
195 			if (fcf_wwn == domain->fcf_wwn) {
196 				break;
197 			}
198 		}
199 	ocs_device_unlock(ocs);
200 	return domain;
201 }
202 
203 /**
204  * @brief Allocate a domain object.
205  *
206  * <h3 class="desc">Description</h3>
207  * A domain object is allocated and initialized. It is associated with the
208  * \c ocs argument.
209  *
210  * @param ocs Pointer to the OCS device.
211  * @param fcf_wwn FCF WWN of the domain.
212  *
213  * @return Returns a pointer to the ocs_domain_t object; or NULL.
214  */
215 
216 ocs_domain_t *
217 ocs_domain_alloc(ocs_t *ocs, uint64_t fcf_wwn)
218 {
219 	ocs_domain_t *domain;
220 
221 	ocs_assert(ocs, NULL);
222 
223 	domain = ocs_malloc(ocs, sizeof(*domain), OCS_M_NOWAIT | OCS_M_ZERO);
224 	if (domain) {
225 		domain->ocs = ocs;
226 		domain->instance_index = ocs->domain_instance_count++;
227 		domain->drvsm.app = domain;
228 		ocs_domain_lock_init(domain);
229 		ocs_lock_init(ocs, &domain->lookup_lock, "Domain lookup[%d]", domain->instance_index);
230 
231 		/* Allocate a sparse vector for sport FC_ID's */
232 		domain->lookup = spv_new(ocs);
233 		if (domain->lookup == NULL) {
234 			ocs_log_err(ocs, "spv_new() failed\n");
235 			ocs_free(ocs, domain, sizeof(*domain));
236 			return NULL;
237 		}
238 
239 		ocs_list_init(&domain->sport_list, ocs_sport_t, link);
240 		domain->fcf_wwn = fcf_wwn;
241 		ocs_log_debug(ocs, "Domain allocated: wwn %016" PRIX64 "\n", domain->fcf_wwn);
242 		domain->femul_enable = (ocs->ctrlmask & OCS_CTRLMASK_ENABLE_FABRIC_EMULATION) != 0;
243 
244 		ocs_device_lock(ocs);
245 			/* if this is the first domain, then assign it as the "root" domain */
246 			if (ocs_list_empty(&ocs->domain_list)) {
247 				ocs->domain = domain;
248 			}
249 			ocs_list_add_tail(&ocs->domain_list, domain);
250 		ocs_device_unlock(ocs);
251 
252 		domain->mgmt_functions = &domain_mgmt_functions;
253 	} else {
254 		ocs_log_err(ocs, "domain allocation failed\n");
255 	}
256 
257 	return domain;
258 }
259 
260 /**
261  * @brief Free a domain object.
262  *
263  * <h3 class="desc">Description</h3>
264  * The domain object is freed.
265  *
266  * @param domain Domain object to free.
267  *
268  * @return None.
269  */
270 
271 void
272 ocs_domain_free(ocs_domain_t *domain)
273 {
274 	ocs_t *ocs;
275 
276 	ocs_assert(domain);
277 	ocs_assert(domain->ocs);
278 
279 	/* Hold frames to clear the domain pointer from the xport lookup */
280 	ocs_domain_hold_frames(domain);
281 
282 	ocs = domain->ocs;
283 
284 	ocs_log_debug(ocs, "Domain free: wwn %016" PRIX64 "\n", domain->fcf_wwn);
285 
286 	spv_del(domain->lookup);
287 	domain->lookup = NULL;
288 
289 	ocs_device_lock(ocs);
290 		ocs_list_remove(&ocs->domain_list, domain);
291 		if (domain == ocs->domain) {
292 			/* set global domain to the new head */
293 			ocs->domain = ocs_list_get_head(&ocs->domain_list);
294 			if (ocs->domain) {
295 				ocs_log_debug(ocs, "setting new domain, old=%p new=%p\n",
296 						domain, ocs->domain);
297 			}
298 		}
299 
300 		if (ocs_list_empty(&ocs->domain_list) && ocs->domain_list_empty_cb ) {
301 			(*ocs->domain_list_empty_cb)(ocs, ocs->domain_list_empty_cb_arg);
302 		}
303 	ocs_device_unlock(ocs);
304 
305 	ocs_lock_free(&domain->lookup_lock);
306 
307 	ocs_free(ocs, domain, sizeof(*domain));
308 }
309 
310 /**
311  * @brief Free memory resources of a domain object.
312  *
313  * <h3 class="desc">Description</h3>
314  * After the domain object is freed, its child objects are also freed.
315  *
316  * @param domain Pointer to a domain object.
317  *
318  * @return None.
319  */
320 
321 void
322 ocs_domain_force_free(ocs_domain_t *domain)
323 {
324 	ocs_sport_t *sport;
325 	ocs_sport_t *next;
326 
327 	/* Shutdown domain sm */
328 	ocs_sm_disable(&domain->drvsm);
329 
330 	ocs_scsi_notify_domain_force_free(domain);
331 
332 	ocs_domain_lock(domain);
333 		ocs_list_foreach_safe(&domain->sport_list, sport, next) {
334 			ocs_sport_force_free(sport);
335 		}
336 	ocs_domain_unlock(domain);
337 	ocs_hw_domain_force_free(&domain->ocs->hw, domain);
338 	ocs_domain_free(domain);
339 }
340 
341 /**
342  * @brief Register a callback when the domain_list goes empty.
343  *
344  * <h3 class="desc">Description</h3>
345  * A function callback may be registered when the domain_list goes empty.
346  *
347  * @param ocs Pointer to a device object.
348  * @param callback Callback function.
349  * @param arg Callback argument.
350  *
351  * @return None.
352  */
353 
354 void
355 ocs_register_domain_list_empty_cb(ocs_t *ocs, void (*callback)(ocs_t *ocs, void *arg), void *arg)
356 {
357 	ocs_device_lock(ocs);
358 		ocs->domain_list_empty_cb = callback;
359 		ocs->domain_list_empty_cb_arg = arg;
360 		if (ocs_list_empty(&ocs->domain_list) && callback) {
361 			(*callback)(ocs, arg);
362 		}
363 	ocs_device_unlock(ocs);
364 }
365 
366 /**
367  * @brief Return a pointer to the domain, given the instance index.
368  *
369  * <h3 class="desc">Description</h3>
370  * A pointer to the domain context, given by the index, is returned.
371  *
372  * @param ocs Pointer to the driver instance context.
373  * @param index Instance index.
374  *
375  * @return Returns a pointer to the domain; or NULL.
376  */
377 
378 ocs_domain_t *
379 ocs_domain_get_instance(ocs_t *ocs, uint32_t index)
380 {
381 	ocs_domain_t *domain = NULL;
382 
383 	if (index >= OCS_MAX_DOMAINS) {
384 		ocs_log_err(ocs, "invalid index: %d\n", index);
385 		return NULL;
386 	}
387 	ocs_device_lock(ocs);
388 		ocs_list_foreach(&ocs->domain_list, domain) {
389 			if (domain->instance_index == index) {
390 				break;
391 			}
392 		}
393 	ocs_device_unlock(ocs);
394 	return domain;
395 }
396 
397 /**
398  * @ingroup domain_sm
399  * @brief Domain state machine: Common event handler.
400  *
401  * <h3 class="desc">Description</h3>
402  * Common/shared events are handled here for the domain state machine.
403  *
404  * @param funcname Function name text.
405  * @param ctx Domain state machine context.
406  * @param evt Event to process.
407  * @param arg Per event optional argument.
408  *
409  * @return Returns NULL.
410  */
411 
412 static void *
413 __ocs_domain_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
414 {
415 	ocs_domain_t *domain = ctx->app;
416 
417 	switch(evt) {
418 	case OCS_EVT_ENTER:
419 	case OCS_EVT_REENTER:
420 	case OCS_EVT_EXIT:
421 	case OCS_EVT_ALL_CHILD_NODES_FREE:
422 		/* this can arise if an FLOGI fails on the SPORT, and the SPORT is shutdown */
423 		break;
424 	default:
425 		ocs_log_warn(domain->ocs, "%-20s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
426 		break;
427 	}
428 
429 	return NULL;
430 }
431 
432 /**
433  * @ingroup domain_sm
434  * @brief Domain state machine: Common shutdown.
435  *
436  * <h3 class="desc">Description</h3>
437  * Handles common shutdown events.
438  *
439  * @param funcname Function name text.
440  * @param ctx Remote node state machine context.
441  * @param evt Event to process.
442  * @param arg Per event optional argument.
443  *
444  * @return Returns NULL.
445  */
446 
447 static void *
448 __ocs_domain_common_shutdown(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
449 {
450 	ocs_domain_t *domain = ctx->app;
451 
452 	switch(evt) {
453 	case OCS_EVT_ENTER:
454 	case OCS_EVT_REENTER:
455 	case OCS_EVT_EXIT:
456 		break;
457 	case OCS_EVT_DOMAIN_FOUND:
458 		ocs_assert(arg, NULL);
459 		/* sm: / save drec, mark domain_found_pending */
460 		ocs_memcpy(&domain->pending_drec, arg, sizeof(domain->pending_drec));
461 		domain->domain_found_pending = TRUE;
462 		break;
463 	case OCS_EVT_DOMAIN_LOST:
464 		/* clear drec available
465 		 * sm: unmark domain_found_pending */
466 		domain->domain_found_pending = FALSE;
467 		break;
468 
469 	default:
470 		ocs_log_warn(domain->ocs, "%-20s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
471 		break;
472 	}
473 
474 	return NULL;
475 }
476 
477 #define std_domain_state_decl(...) \
478 	ocs_domain_t *domain = NULL; \
479 	ocs_t *ocs = NULL; \
480 	\
481 	ocs_assert(ctx, NULL); \
482 	ocs_assert(ctx->app, NULL); \
483 	domain = ctx->app; \
484 	ocs_assert(domain->ocs, NULL); \
485 	ocs = domain->ocs; \
486 	ocs_assert(ocs->xport, NULL);
487 
488 /**
489  * @ingroup domain_sm
490  * @brief Domain state machine: Initial state.
491  *
492  * <h3 class="desc">Description</h3>
493  * The initial state for a domain. Each domain is initialized to
494  * this state at start of day (SOD).
495  *
496  * @param ctx Domain state machine context.
497  * @param evt Event to process.
498  * @param arg Per event optional argument.
499  *
500  * @return Returns NULL.
501  */
502 
503 void *
504 __ocs_domain_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
505 {
506 	std_domain_state_decl();
507 
508 	domain_sm_trace(domain);
509 
510 	switch(evt) {
511 	case OCS_EVT_ENTER:
512 		domain->attached = 0;
513 		break;
514 
515 	case OCS_EVT_DOMAIN_FOUND: {
516 		int32_t		vlan = 0;
517 		uint32_t	i;
518 		ocs_domain_record_t *drec = arg;
519 		ocs_sport_t *sport;
520 
521 		uint64_t	my_wwnn = ocs->xport->req_wwnn;
522 		uint64_t	my_wwpn = ocs->xport->req_wwpn;
523 		uint64_t	be_wwpn;
524 
525 		/* For now, user must specify both port name and node name, or we let firmware
526 		 * pick both (same as for vports).
527 		 * TODO: do we want to allow setting only port name or only node name?
528 		 */
529 		if ((my_wwpn == 0) || (my_wwnn == 0)) {
530 			ocs_log_debug(ocs, "using default hardware WWN configuration \n");
531 			my_wwpn = ocs_get_wwn(&ocs->hw, OCS_HW_WWN_PORT);
532 			my_wwnn = ocs_get_wwn(&ocs->hw, OCS_HW_WWN_NODE);
533 		}
534 
535 		ocs_log_debug(ocs, "Creating base sport using WWPN %016" PRIx64 " WWNN %016" PRIx64 "\n",
536 			my_wwpn, my_wwnn);
537 
538 		/* Allocate a sport and transition to __ocs_sport_allocated */
539 		sport = ocs_sport_alloc(domain, my_wwpn, my_wwnn, UINT32_MAX, ocs->enable_ini, ocs->enable_tgt);
540 
541 		if (sport == NULL) {
542 			ocs_log_err(ocs, "ocs_sport_alloc() failed\n");
543 			break;
544 		}
545 		ocs_sm_transition(&sport->sm, __ocs_sport_allocated, NULL);
546 
547 		/* If domain is ethernet, then fetch the vlan id value */
548 		if (drec->is_ethernet) {
549 			vlan = ocs_bitmap_search((void *)drec->map.vlan, TRUE, 512 * 8);
550 			if (vlan < 0) {
551 				ocs_log_err(ocs, "no VLAN id available (FCF=%d)\n",
552 						drec->index);
553 				break;
554 			}
555 		}
556 
557 		be_wwpn = ocs_htobe64(sport->wwpn);
558 
559 		/* allocate ocs_sli_port_t object for local port
560 		 * Note: drec->fc_id is ALPA from read_topology only if loop
561 		 */
562 		if (ocs_hw_port_alloc(&ocs->hw, sport, NULL, (uint8_t *)&be_wwpn)) {
563 			ocs_log_err(ocs, "Can't allocate port\n");
564 			ocs_sport_free(sport);
565 			break;
566 		}
567 
568 		/* initialize domain object */
569 		domain->is_loop = drec->is_loop;
570 		domain->is_fc = drec->is_fc;
571 
572 		/*
573 		 * If the loop position map includes ALPA == 0, then we are in a public loop (NL_PORT)
574 		 * Note that the first element of the loopmap[] contains the count of elements, and if
575 		 * ALPA == 0 is present, it will occupy the first location after the count.
576 		 */
577 		domain->is_nlport = drec->map.loop[1] == 0x00;
578 
579 		if (domain->is_loop) {
580 			ocs_log_debug(ocs, "%s fc_id=%#x speed=%d\n",
581 					drec->is_loop ? (domain->is_nlport ? "public-loop" : "loop") : "other",
582 					drec->fc_id, drec->speed);
583 
584 			sport->fc_id = drec->fc_id;
585 			sport->topology = OCS_SPORT_TOPOLOGY_LOOP;
586 			ocs_snprintf(sport->display_name, sizeof(sport->display_name), "s%06x", drec->fc_id);
587 
588 			if (ocs->enable_ini) {
589 				uint32_t count = drec->map.loop[0];
590 				ocs_log_debug(ocs, "%d position map entries\n", count);
591 				for (i = 1; i <= count; i++) {
592 					if (drec->map.loop[i] != drec->fc_id) {
593 						ocs_node_t *node;
594 
595 						ocs_log_debug(ocs, "%#x -> %#x\n",
596 								drec->fc_id, drec->map.loop[i]);
597 						node = ocs_node_alloc(sport, drec->map.loop[i], FALSE, TRUE);
598 						if (node == NULL) {
599 							ocs_log_err(ocs, "ocs_node_alloc() failed\n");
600 							break;
601 						}
602 						ocs_node_transition(node, __ocs_d_wait_loop, NULL);
603 					}
604 				}
605 			}
606 		}
607 
608 		/* Initiate HW domain alloc */
609 		if (ocs_hw_domain_alloc(&ocs->hw, domain, drec->index, vlan)) {
610 			ocs_log_err(ocs, "Failed to initiate HW domain allocation\n");
611 			break;
612 		}
613 		ocs_sm_transition(ctx, __ocs_domain_wait_alloc, arg);
614 		break;
615 	}
616 	default:
617 		__ocs_domain_common(__func__, ctx, evt, arg);
618 		return NULL;
619 	}
620 
621 	return NULL;
622 }
623 
624 /**
625  * @ingroup domain_sm
626  * @brief Domain state machine: Wait for the domain allocation to complete.
627  *
628  * <h3 class="desc">Description</h3>
629  * Waits for the domain state to be allocated. After the HW domain
630  * allocation process has been initiated, this state waits for
631  * that process to complete (i.e. a domain-alloc-ok event).
632  *
633  * @param ctx Domain state machine context.
634  * @param evt Event to process.
635  * @param arg Per event optional argument.
636  *
637  * @return Returns NULL.
638  */
639 
640 void *
641 __ocs_domain_wait_alloc(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
642 {
643 	ocs_sport_t *sport;
644 	std_domain_state_decl();
645 
646 	domain_sm_trace(domain);
647 
648 	switch(evt) {
649 	case OCS_EVT_DOMAIN_ALLOC_OK: {
650 		char prop_buf[32];
651 		uint64_t wwn_bump = 0;
652 		fc_plogi_payload_t *sp;
653 
654 		if (ocs_get_property("wwn_bump", prop_buf, sizeof(prop_buf)) == 0) {
655 			wwn_bump = ocs_strtoull(prop_buf, 0, 0);
656 		}
657 
658 		sport = domain->sport;
659 		ocs_assert(sport, NULL);
660 		sp = (fc_plogi_payload_t*) sport->service_params;
661 
662 		/* Save the domain service parameters */
663 		ocs_memcpy(domain->service_params + 4, domain->dma.virt, sizeof(fc_plogi_payload_t) - 4);
664 		ocs_memcpy(sport->service_params + 4, domain->dma.virt, sizeof(fc_plogi_payload_t) - 4);
665 
666 		/* If we're in fabric emulation mode, the flogi service parameters have not been setup yet,
667 		 * so we need some reasonable BB credit value
668 		 */
669 		if (domain->femul_enable) {
670 			ocs_memcpy(domain->flogi_service_params + 4, domain->service_params + 4, sizeof(fc_plogi_payload_t) - 4);
671 		}
672 
673 		/* Update the sport's service parameters, user might have specified non-default names */
674 		sp->port_name_hi = ocs_htobe32((uint32_t) (sport->wwpn >> 32ll));
675 		sp->port_name_lo = ocs_htobe32((uint32_t) sport->wwpn);
676 		sp->node_name_hi = ocs_htobe32((uint32_t) (sport->wwnn >> 32ll));
677 		sp->node_name_lo = ocs_htobe32((uint32_t) sport->wwnn);
678 
679 		if (wwn_bump) {
680 			sp->port_name_lo = ocs_htobe32(ocs_be32toh(sp->port_name_lo) ^ ((uint32_t)(wwn_bump)));
681 			sp->port_name_hi = ocs_htobe32(ocs_be32toh(sp->port_name_hi) ^ ((uint32_t)(wwn_bump >> 32)));
682 			sp->node_name_lo = ocs_htobe32(ocs_be32toh(sp->node_name_lo) ^ ((uint32_t)(wwn_bump)));
683 			sp->node_name_hi = ocs_htobe32(ocs_be32toh(sp->node_name_hi) ^ ((uint32_t)(wwn_bump >> 32)));
684 			ocs_log_info(ocs, "Overriding WWN\n");
685 		}
686 
687 		/* Take the loop topology path, unless we are an NL_PORT (public loop) */
688 		if (domain->is_loop && !domain->is_nlport) {
689 			/*
690 			 * For loop, we already have our FC ID and don't need fabric login.
691 			 * Transition to the allocated state and post an event to attach to
692 			 * the domain. Note that this breaks the normal action/transition
693 			 * pattern here to avoid a race with the domain attach callback.
694 			 */
695 			/* sm: is_loop / domain_attach */
696 			ocs_sm_transition(ctx, __ocs_domain_allocated, NULL);
697 			__ocs_domain_attach_internal(domain, sport->fc_id);
698 			break;
699 		} else {
700 			ocs_node_t *node;
701 
702 			/* alloc fabric node, send FLOGI */
703 			node = ocs_node_find(sport, FC_ADDR_FABRIC);
704 			if (node) {
705 				ocs_log_err(ocs, "Hmmmm ... Fabric Controller node already exists\n");
706 				break;
707 			}
708 			node = ocs_node_alloc(sport, FC_ADDR_FABRIC, FALSE, FALSE);
709 			if (!node) {
710 				ocs_log_err(ocs, "Error: ocs_node_alloc() failed\n");
711 			} else {
712 				if (ocs->nodedb_mask & OCS_NODEDB_PAUSE_FABRIC_LOGIN) {
713 					ocs_node_pause(node, __ocs_fabric_init);
714 				} else {
715 					ocs_node_transition(node, __ocs_fabric_init, NULL);
716 				}
717 			}
718 			/* Accept frames */
719 			domain->req_accept_frames = 1;
720 		}
721 		/* sm: start fabric logins */
722 		ocs_sm_transition(ctx, __ocs_domain_allocated, NULL);
723 		break;
724 	}
725 
726 	case OCS_EVT_DOMAIN_ALLOC_FAIL:
727 		/* TODO: hw/device reset */
728 		ocs_log_err(ocs, "%s recv'd waiting for DOMAIN_ALLOC_OK; shutting down domain\n",
729 			ocs_sm_event_name(evt));
730 		domain->req_domain_free = 1;
731 		break;
732 
733 	case OCS_EVT_DOMAIN_FOUND:
734 		/* Should not happen */
735 		ocs_assert(evt, NULL);
736 		break;
737 
738 	case OCS_EVT_DOMAIN_LOST:
739 		ocs_log_debug(ocs, "%s received while waiting for ocs_hw_domain_alloc() to complete\n", ocs_sm_event_name(evt));
740 		ocs_sm_transition(ctx, __ocs_domain_wait_domain_lost, NULL);
741 		break;
742 
743 	default:
744 		__ocs_domain_common(__func__, ctx, evt, arg);
745 		return NULL;
746 	}
747 
748 	return NULL;
749 }
750 
751 /**
752  * @ingroup domain_sm
753  * @brief Domain state machine: Wait for the domain attach request.
754  *
755  * <h3 class="desc">Description</h3>
756  * In this state, the domain has been allocated and is waiting for a domain attach request.
757  * The attach request comes from a node instance completing the fabric login,
758  * or from a point-to-point negotiation and login.
759  *
760  * @param ctx Remote node state machine context.
761  * @param evt Event to process.
762  * @param arg Per event optional argument.
763  *
764  * @return Returns NULL.
765  */
766 
767 void *
768 __ocs_domain_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
769 {
770 	int32_t rc = 0;
771 	std_domain_state_decl();
772 
773 	domain_sm_trace(domain);
774 
775 	switch(evt) {
776 	case OCS_EVT_DOMAIN_REQ_ATTACH: {
777 		uint32_t fc_id;
778 
779 		ocs_assert(arg, NULL);
780 
781 		fc_id = *((uint32_t*)arg);
782 		ocs_log_debug(ocs, "Requesting hw domain attach fc_id x%x\n", fc_id);
783 		/* Update sport lookup */
784 		ocs_lock(&domain->lookup_lock);
785 			spv_set(domain->lookup, fc_id, domain->sport);
786 		ocs_unlock(&domain->lookup_lock);
787 
788 		/* Update display name for the sport */
789 		ocs_node_fcid_display(fc_id, domain->sport->display_name, sizeof(domain->sport->display_name));
790 
791 		/* Issue domain attach call */
792 		rc = ocs_hw_domain_attach(&ocs->hw, domain, fc_id);
793 		if (rc) {
794 			ocs_log_err(ocs, "ocs_hw_domain_attach failed: %d\n", rc);
795 			return NULL;
796 		}
797 		/* sm: / domain_attach */
798 		ocs_sm_transition(ctx, __ocs_domain_wait_attach, NULL);
799 		break;
800 	}
801 
802 	case OCS_EVT_DOMAIN_FOUND:
803 		/* Should not happen */
804 		ocs_assert(evt, NULL);
805 		break;
806 
807 	case OCS_EVT_DOMAIN_LOST: {
808 		int32_t rc;
809 		ocs_log_debug(ocs, "%s received while waiting for OCS_EVT_DOMAIN_REQ_ATTACH\n",
810 			ocs_sm_event_name(evt));
811 		ocs_domain_lock(domain);
812 		if (!ocs_list_empty(&domain->sport_list)) {
813 			/* if there are sports, transition to wait state and
814 			 * send shutdown to each sport */
815 			ocs_sport_t	*sport = NULL;
816 			ocs_sport_t	*sport_next = NULL;
817 			ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
818 			ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
819 				ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
820 			}
821 			ocs_domain_unlock(domain);
822 		} else {
823 			ocs_domain_unlock(domain);
824 			/* no sports exist, free domain */
825 			ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
826 			rc = ocs_hw_domain_free(&ocs->hw, domain);
827 			if (rc) {
828 				ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
829 				/* TODO: hw/device reset needed */
830 			}
831 		}
832 
833 		break;
834 	}
835 
836 	default:
837 		__ocs_domain_common(__func__, ctx, evt, arg);
838 		return NULL;
839 	}
840 
841 	return NULL;
842 }
843 
844 /**
845  * @ingroup domain_sm
846  * @brief Domain state machine: Wait for the HW domain attach to complete.
847  *
848  * <h3 class="desc">Description</h3>
849  * Waits for the HW domain attach to complete. Forwards attach ok event to the
850  * fabric node state machine.
851  *
852  * @param ctx Remote node state machine context.
853  * @param evt Event to process.
854  * @param arg Per event optional argument.
855  *
856  * @return Returns NULL.
857  */
858 
859 void *
860 __ocs_domain_wait_attach(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
861 {
862 	std_domain_state_decl();
863 
864 	domain_sm_trace(domain);
865 
866 	switch(evt) {
867 	case OCS_EVT_DOMAIN_ATTACH_OK: {
868 		ocs_node_t *node = NULL;
869 		ocs_node_t *next_node = NULL;
870 		ocs_sport_t *sport;
871 		ocs_sport_t *next_sport;
872 
873 		/* Mark as attached */
874 		domain->attached = 1;
875 
876 		/* Register with SCSI API */
877 		if (ocs->enable_tgt)
878 			ocs_scsi_tgt_new_domain(domain);
879 		if (ocs->enable_ini)
880 			ocs_scsi_ini_new_domain(domain);
881 
882 		/* Transition to ready */
883 		/* sm: / forward event to all sports and nodes */
884 		ocs_sm_transition(ctx, __ocs_domain_ready, NULL);
885 
886 		/* We have an FCFI, so we can accept frames */
887 		domain->req_accept_frames = 1;
888 		/* Set domain notify pending state to avoid duplicate domain event post */
889 		domain->domain_notify_pend = 1;
890 
891 		/* Notify all nodes that the domain attach request has completed
892 		 * Note: sport will have already received notification of sport attached
893 		 * as a result of the HW's port attach.
894 		 */
895 		ocs_domain_lock(domain);
896 			ocs_list_foreach_safe(&domain->sport_list, sport, next_sport) {
897 				ocs_sport_lock(sport);
898 					ocs_list_foreach_safe(&sport->node_list, node, next_node) {
899 						ocs_node_post_event(node, OCS_EVT_DOMAIN_ATTACH_OK, NULL);
900 					}
901 				ocs_sport_unlock(sport);
902 			}
903 		ocs_domain_unlock(domain);
904 		domain->domain_notify_pend = 0;
905 		break;
906 	}
907 
908 	case OCS_EVT_DOMAIN_ATTACH_FAIL:
909 		ocs_log_debug(ocs, "%s received while waiting for hw attach to complete\n", ocs_sm_event_name(evt));
910 		/* TODO: hw/device reset */
911 		break;
912 
913 	case OCS_EVT_DOMAIN_FOUND:
914 		/* Should not happen */
915 		ocs_assert(evt, NULL);
916 		break;
917 
918 	case OCS_EVT_DOMAIN_LOST:
919 		/* Domain lost while waiting for an attach to complete, go to a state that waits for
920 		 * the domain attach to complete, then handle domain lost
921 		 */
922 		ocs_sm_transition(ctx, __ocs_domain_wait_domain_lost, NULL);
923 		break;
924 
925 	case OCS_EVT_DOMAIN_REQ_ATTACH:
926 		/* In P2P we can get an attach request from the other FLOGI path, so drop this one */
927 		break;
928 
929 	default:
930 		__ocs_domain_common(__func__, ctx, evt, arg);
931 		return NULL;
932 	}
933 
934 	return NULL;
935 }
936 
937 /**
938  * @ingroup domain_sm
939  * @brief Domain state machine: Ready state.
940  *
941  * <h3 class="desc">Description</h3>
942  * This is a domain ready state. It waits for a domain-lost event, and initiates shutdown.
943  *
944  * @param ctx Remote node state machine context.
945  * @param evt Event to process.
946  * @param arg Per event optional argument.
947  *
948  * @return Returns NULL.
949  */
950 
951 void *
952 __ocs_domain_ready(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
953 {
954 	std_domain_state_decl();
955 
956 	domain_sm_trace(domain);
957 
958 	switch(evt) {
959 	case OCS_EVT_ENTER: {
960 		/* start any pending vports */
961 		if (ocs_vport_start(domain)) {
962 			ocs_log_debug(domain->ocs, "ocs_vport_start() did not start all vports\n");
963 		}
964 		break;
965 	}
966 	case OCS_EVT_DOMAIN_LOST: {
967 		int32_t rc;
968 		ocs_domain_lock(domain);
969 		if (!ocs_list_empty(&domain->sport_list)) {
970 			/* if there are sports, transition to wait state and send
971 			* shutdown to each sport */
972 			ocs_sport_t	*sport = NULL;
973 			ocs_sport_t	*sport_next = NULL;
974 			ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
975 			ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
976 				ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
977 			}
978 			ocs_domain_unlock(domain);
979 		} else {
980 			ocs_domain_unlock(domain);
981 			/* no sports exist, free domain */
982 			ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
983 			rc = ocs_hw_domain_free(&ocs->hw, domain);
984 			if (rc) {
985 				ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
986 				/* TODO: hw/device reset needed */
987 			}
988 		}
989 		break;
990 	}
991 
992 	case OCS_EVT_DOMAIN_FOUND:
993 		/* Should not happen */
994 		ocs_assert(evt, NULL);
995 		break;
996 
997 	case OCS_EVT_DOMAIN_REQ_ATTACH: {
998 		/* can happen during p2p */
999 		uint32_t fc_id;
1000 
1001 		ocs_assert(arg, NULL);
1002 		fc_id = *((uint32_t*)arg);
1003 
1004 		/* Assume that the domain is attached */
1005 		ocs_assert(domain->attached, NULL);
1006 
1007 		/* Verify that the requested FC_ID is the same as the one we're working with */
1008 		ocs_assert(domain->sport->fc_id == fc_id, NULL);
1009 		break;
1010 	}
1011 
1012 	default:
1013 		__ocs_domain_common(__func__, ctx, evt, arg);
1014 		return NULL;
1015 	}
1016 
1017 	return NULL;
1018 }
1019 
1020 /**
1021  * @ingroup domain_sm
1022  * @brief Domain state machine: Wait for nodes to free prior to the domain shutdown.
1023  *
1024  * <h3 class="desc">Description</h3>
1025  * All nodes are freed, and ready for a domain shutdown.
1026  *
1027  * @param ctx Remote node sm context.
1028  * @param evt Event to process.
1029  * @param arg Per event optional argument.
1030  *
1031  * @return Returns NULL.
1032  */
1033 
1034 void *
1035 __ocs_domain_wait_sports_free(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
1036 {
1037 	std_domain_state_decl();
1038 
1039 	domain_sm_trace(domain);
1040 
1041 	switch(evt) {
1042 	case OCS_EVT_ALL_CHILD_NODES_FREE: {
1043 		int32_t rc;
1044 
1045 		/* sm: ocs_hw_domain_free */
1046 		ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
1047 
1048 		/* Request ocs_hw_domain_free and wait for completion */
1049 		rc = ocs_hw_domain_free(&ocs->hw, domain);
1050 		if (rc) {
1051 			ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
1052 		}
1053 		break;
1054 	}
1055 	default:
1056 		__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
1057 		return NULL;
1058 	}
1059 
1060 	return NULL;
1061 }
1062 
1063 /**
1064  * @ingroup domain_sm
1065  * @brief Domain state machine: Complete the domain shutdown.
1066  *
1067  * <h3 class="desc">Description</h3>
1068  * Waits for a HW domain free to complete.
1069  *
1070  * @param ctx Remote node state machine context.
1071  * @param evt Event to process.
1072  * @param arg Per event optional argument.
1073  *
1074  * @return Returns NULL.
1075  */
1076 
1077 void *
1078 __ocs_domain_wait_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
1079 {
1080 	std_domain_state_decl();
1081 
1082 	domain_sm_trace(domain);
1083 
1084 	switch(evt) {
1085 	case OCS_EVT_DOMAIN_FREE_OK: {
1086 		if (ocs->enable_ini)
1087 			ocs_scsi_ini_del_domain(domain);
1088 		if (ocs->enable_tgt)
1089 			ocs_scsi_tgt_del_domain(domain);
1090 
1091 		/* sm: domain_free */
1092 		if (domain->domain_found_pending) {
1093 			/* save fcf_wwn and drec from this domain, free current domain and allocate
1094 			 * a new one with the same fcf_wwn
1095 			 * TODO: could use a SLI-4 "re-register VPI" operation here
1096 			 */
1097 			uint64_t fcf_wwn = domain->fcf_wwn;
1098 			ocs_domain_record_t drec = domain->pending_drec;
1099 
1100 			ocs_log_debug(ocs, "Reallocating domain\n");
1101 			domain->req_domain_free = 1;
1102 			domain = ocs_domain_alloc(ocs, fcf_wwn);
1103 
1104 			if (domain == NULL) {
1105 				ocs_log_err(ocs, "ocs_domain_alloc() failed\n");
1106 				/* TODO: hw/device reset needed */
1107 				return NULL;
1108 			}
1109 			/*
1110 			 * got a new domain; at this point, there are at least two domains
1111 			 * once the req_domain_free flag is processed, the associated domain
1112 			 * will be removed.
1113 			 */
1114 			ocs_sm_transition(&domain->drvsm, __ocs_domain_init, NULL);
1115 			ocs_sm_post_event(&domain->drvsm, OCS_EVT_DOMAIN_FOUND, &drec);
1116 		} else {
1117 			domain->req_domain_free = 1;
1118 		}
1119 		break;
1120 	}
1121 
1122 	default:
1123 		__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
1124 		return NULL;
1125 	}
1126 
1127 	return NULL;
1128 }
1129 
1130 /**
1131  * @ingroup domain_sm
1132  * @brief Domain state machine: Wait for the domain alloc/attach completion
1133  * after receiving a domain lost.
1134  *
1135  * <h3 class="desc">Description</h3>
1136  * This state is entered when receiving a domain lost while waiting for a domain alloc
1137  * or a domain attach to complete.
1138  *
1139  * @param ctx Remote node state machine context.
1140  * @param evt Event to process.
1141  * @param arg Per event optional argument.
1142  *
1143  * @return Returns NULL.
1144  */
1145 
1146 void *
1147 __ocs_domain_wait_domain_lost(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
1148 {
1149 	std_domain_state_decl();
1150 
1151 	domain_sm_trace(domain);
1152 
1153 	switch(evt) {
1154 	case OCS_EVT_DOMAIN_ALLOC_OK:
1155 	case OCS_EVT_DOMAIN_ATTACH_OK: {
1156 		int32_t rc;
1157 		ocs_domain_lock(domain);
1158 		if (!ocs_list_empty(&domain->sport_list)) {
1159 			/* if there are sports, transition to wait state and send
1160 			* shutdown to each sport */
1161 			ocs_sport_t	*sport = NULL;
1162 			ocs_sport_t	*sport_next = NULL;
1163 			ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
1164 			ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
1165 				ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
1166 			}
1167 			ocs_domain_unlock(domain);
1168 		} else {
1169 			ocs_domain_unlock(domain);
1170 			/* no sports exist, free domain */
1171 			ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
1172 			rc = ocs_hw_domain_free(&ocs->hw, domain);
1173 			if (rc) {
1174 				ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
1175 				/* TODO: hw/device reset needed */
1176 			}
1177 		}
1178 		break;
1179 	}
1180 	case OCS_EVT_DOMAIN_ALLOC_FAIL:
1181 	case OCS_EVT_DOMAIN_ATTACH_FAIL:
1182 		ocs_log_err(ocs, "[domain] %-20s: failed\n", ocs_sm_event_name(evt));
1183 		/* TODO: hw/device reset needed */
1184 		break;
1185 
1186 	default:
1187 		__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
1188 		return NULL;
1189 	}
1190 
1191 	return NULL;
1192 }
1193 
1194 /**
1195  * @brief Save the port's service parameters.
1196  *
1197  * <h3 class="desc">Description</h3>
1198  * Service parameters from the fabric FLOGI are saved in the domain's
1199  * flogi_service_params array.
1200  *
1201  * @param domain Pointer to the domain.
1202  * @param payload Service parameters to save.
1203  *
1204  * @return None.
1205  */
1206 
1207 void
1208 ocs_domain_save_sparms(ocs_domain_t *domain, void *payload)
1209 {
1210 	ocs_memcpy(domain->flogi_service_params, payload, sizeof (fc_plogi_payload_t));
1211 }
1212 /**
1213  * @brief Initiator domain attach. (internal call only)
1214  *
1215  * Assumes that the domain SM lock is already locked
1216  *
1217  * <h3 class="desc">Description</h3>
1218  * The HW domain attach function is started.
1219  *
1220  * @param domain Pointer to the domain object.
1221  * @param s_id FC_ID of which to register this domain.
1222  *
1223  * @return None.
1224  */
1225 
1226 void
1227 __ocs_domain_attach_internal(ocs_domain_t *domain, uint32_t s_id)
1228 {
1229 	ocs_memcpy(domain->dma.virt, ((uint8_t*)domain->flogi_service_params)+4, sizeof (fc_plogi_payload_t) - 4);
1230 	(void)ocs_sm_post_event(&domain->drvsm, OCS_EVT_DOMAIN_REQ_ATTACH, &s_id);
1231 }
1232 
1233 /**
1234  * @brief Initiator domain attach.
1235  *
1236  * <h3 class="desc">Description</h3>
1237  * The HW domain attach function is started.
1238  *
1239  * @param domain Pointer to the domain object.
1240  * @param s_id FC_ID of which to register this domain.
1241  *
1242  * @return None.
1243  */
1244 
1245 void
1246 ocs_domain_attach(ocs_domain_t *domain, uint32_t s_id)
1247 {
1248 	__ocs_domain_attach_internal(domain, s_id);
1249 }
1250 
1251 int
1252 ocs_domain_post_event(ocs_domain_t *domain, ocs_sm_event_t event, void *arg)
1253 {
1254 	int rc;
1255 	int accept_frames;
1256 	int req_domain_free;
1257 
1258 	rc = ocs_sm_post_event(&domain->drvsm, event, arg);
1259 
1260 	req_domain_free = domain->req_domain_free;
1261 	domain->req_domain_free = 0;
1262 
1263 	accept_frames = domain->req_accept_frames;
1264 	domain->req_accept_frames = 0;
1265 
1266 	if (accept_frames) {
1267 		ocs_domain_accept_frames(domain);
1268 	}
1269 
1270 	if (req_domain_free) {
1271 		ocs_domain_free(domain);
1272 	}
1273 
1274 	return rc;
1275 }
1276 
1277 /**
1278  * @brief Return the WWN as a uint64_t.
1279  *
1280  * <h3 class="desc">Description</h3>
1281  * Calls the HW property function for the WWNN or WWPN, and returns the value
1282  * as a uint64_t.
1283  *
1284  * @param hw Pointer to the HW object.
1285  * @param prop HW property.
1286  *
1287  * @return Returns uint64_t request value.
1288  */
1289 
1290 uint64_t
1291 ocs_get_wwn(ocs_hw_t *hw, ocs_hw_property_e prop)
1292 {
1293 	uint8_t *p = ocs_hw_get_ptr(hw, prop);
1294 	uint64_t value = 0;
1295 
1296 	if (p) {
1297 		uint32_t i;
1298 		for (i = 0; i < sizeof(value); i++) {
1299 			value = (value << 8) | p[i];
1300 		}
1301 	}
1302 	return value;
1303 }
1304 
1305 /**
1306  * @brief Generate a domain ddump.
1307  *
1308  * <h3 class="desc">Description</h3>
1309  * Generates a domain ddump.
1310  *
1311  * @param textbuf Pointer to the text buffer.
1312  * @param domain Pointer to the domain context.
1313  *
1314  * @return Returns 0 on success, or a negative value on failure.
1315  */
1316 
1317 int
1318 ocs_ddump_domain(ocs_textbuf_t *textbuf, ocs_domain_t *domain)
1319 {
1320 	ocs_sport_t *sport;
1321 	int retval = 0;
1322 
1323 	ocs_ddump_section(textbuf, "domain", domain->instance_index);
1324 	ocs_ddump_value(textbuf, "display_name", "%s", domain->display_name);
1325 
1326 	ocs_ddump_value(textbuf, "fcf", "%#x", domain->fcf);
1327 	ocs_ddump_value(textbuf, "fcf_indicator", "%#x", domain->fcf_indicator);
1328 	ocs_ddump_value(textbuf, "vlan_id", "%#x", domain->vlan_id);
1329 	ocs_ddump_value(textbuf, "indicator", "%#x", domain->indicator);
1330 	ocs_ddump_value(textbuf, "attached", "%d", domain->attached);
1331 	ocs_ddump_value(textbuf, "is_loop", "%d", domain->is_loop);
1332 	ocs_ddump_value(textbuf, "is_nlport", "%d", domain->is_nlport);
1333 
1334 	ocs_scsi_ini_ddump(textbuf, OCS_SCSI_DDUMP_DOMAIN, domain);
1335 	ocs_scsi_tgt_ddump(textbuf, OCS_SCSI_DDUMP_DOMAIN, domain);
1336 
1337 	ocs_display_sparams(NULL, "domain_sparms", 1, textbuf, domain->dma.virt);
1338 
1339 	if (ocs_domain_lock_try(domain) != TRUE) {
1340 		/* Didn't get the lock */
1341 		return -1;
1342 	}
1343 		ocs_list_foreach(&domain->sport_list, sport) {
1344 			retval = ocs_ddump_sport(textbuf, sport);
1345 			if (retval != 0) {
1346 				break;
1347 			}
1348 		}
1349 
1350 #if defined(ENABLE_FABRIC_EMULATION)
1351 		ocs_ddump_ns(textbuf, domain->ocs_ns);
1352 #endif
1353 
1354 	ocs_domain_unlock(domain);
1355 
1356 	ocs_ddump_endsection(textbuf, "domain", domain->instance_index);
1357 
1358 	return retval;
1359 }
1360 
1361 void
1362 ocs_mgmt_domain_list(ocs_textbuf_t *textbuf, void *object)
1363 {
1364 	ocs_sport_t *sport;
1365 	ocs_domain_t *domain = (ocs_domain_t *)object;
1366 
1367 	ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
1368 
1369 	/* Add my status values to textbuf */
1370 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "fcf");
1371 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "fcf_indicator");
1372 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "vlan_id");
1373 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "indicator");
1374 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "attached");
1375 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "is_loop");
1376 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "display_name");
1377 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "num_sports");
1378 #if defined(ENABLE_FABRIC_EMULATION)
1379 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RW, "femul_enable");
1380 #endif
1381 
1382 	if (ocs_domain_lock_try(domain) == TRUE) {
1383 		/* If we get here, then we are holding the domain lock */
1384 		ocs_list_foreach(&domain->sport_list, sport) {
1385 			if ((sport->mgmt_functions) && (sport->mgmt_functions->get_list_handler)) {
1386 				sport->mgmt_functions->get_list_handler(textbuf, sport);
1387 			}
1388 		}
1389 		ocs_domain_unlock(domain);
1390 	}
1391 
1392 	ocs_mgmt_end_section(textbuf, "domain", domain->instance_index);
1393 }
1394 
1395 int
1396 ocs_mgmt_domain_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *object)
1397 {
1398 	ocs_sport_t *sport;
1399 	ocs_domain_t *domain = (ocs_domain_t *)object;
1400 	char qualifier[80];
1401 	int retval = -1;
1402 
1403 	ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
1404 
1405 	snprintf(qualifier, sizeof(qualifier), "%s/domain[%d]", parent, domain->instance_index);
1406 
1407 	/* If it doesn't start with my qualifier I don't know what to do with it */
1408 	if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
1409 		char *unqualified_name = name + strlen(qualifier) +1;
1410 
1411 		/* See if it's a value I can supply */
1412 		if (ocs_strcmp(unqualified_name, "display_name") == 0) {
1413 			ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
1414 			retval = 0;
1415 		} else if (ocs_strcmp(unqualified_name, "fcf") == 0) {
1416 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf", "%#x", domain->fcf);
1417 			retval = 0;
1418 		} else if (ocs_strcmp(unqualified_name, "fcf_indicator") == 0) {
1419 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf_indicator", "%#x", domain->fcf_indicator);
1420 			retval = 0;
1421 		} else if (ocs_strcmp(unqualified_name, "vlan_id") == 0) {
1422 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "vlan_id", "%#x", domain->vlan_id);
1423 			retval = 0;
1424 		} else if (ocs_strcmp(unqualified_name, "indicator") == 0) {
1425 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "%#x", domain->indicator);
1426 			retval = 0;
1427 		} else if (ocs_strcmp(unqualified_name, "attached") == 0) {
1428 			ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "attached", domain->attached);
1429 			retval = 0;
1430 		} else if (ocs_strcmp(unqualified_name, "is_loop") == 0) {
1431 			ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_loop",  domain->is_loop);
1432 			retval = 0;
1433 		} else if (ocs_strcmp(unqualified_name, "is_nlport") == 0) {
1434 			ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_nlport",  domain->is_nlport);
1435 			retval = 0;
1436 		} else if (ocs_strcmp(unqualified_name, "display_name") == 0) {
1437 			ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
1438 			retval = 0;
1439 #if defined(ENABLE_FABRIC_EMULATION)
1440 		} else if (ocs_strcmp(unqualified_name, "femul_enable") == 0) {
1441 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "femul_enable", "%d", domain->femul_enable);
1442 			retval = 0;
1443 #endif
1444 		} else if (ocs_strcmp(unqualified_name, "num_sports") == 0) {
1445 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "num_sports", "%d", domain->sport_instance_count);
1446 			retval = 0;
1447 		} else {
1448 			/* If I didn't know the value of this status pass the request to each of my children */
1449 
1450 			ocs_domain_lock(domain);
1451 			ocs_list_foreach(&domain->sport_list, sport) {
1452 				if ((sport->mgmt_functions) && (sport->mgmt_functions->get_handler)) {
1453 					retval = sport->mgmt_functions->get_handler(textbuf, qualifier, name, sport);
1454 				}
1455 
1456 				if (retval == 0) {
1457 					break;
1458 				}
1459 			}
1460 			ocs_domain_unlock(domain);
1461 		}
1462 	}
1463 
1464 	ocs_mgmt_end_section(textbuf, "domain", domain->instance_index);
1465 	return retval;
1466 }
1467 
1468 void
1469 ocs_mgmt_domain_get_all(ocs_textbuf_t *textbuf, void *object)
1470 {
1471 	ocs_sport_t *sport;
1472 	ocs_domain_t *domain = (ocs_domain_t *)object;
1473 
1474 	ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
1475 
1476 	ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
1477 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf", "%#x", domain->fcf);
1478 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf_indicator", "%#x", domain->fcf_indicator);
1479 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "vlan_id", "%#x", domain->vlan_id);
1480 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "%#x", domain->indicator);
1481 	ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "attached", domain->attached);
1482 	ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_loop",  domain->is_loop);
1483 	ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_nlport",  domain->is_nlport);
1484 #if defined(ENABLE_FABRIC_EMULATION)
1485 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "femul_enable", "%d", domain->femul_enable);
1486 #endif
1487 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "num_sports",  "%d", domain->sport_instance_count);
1488 
1489 	ocs_domain_lock(domain);
1490 	ocs_list_foreach(&domain->sport_list, sport) {
1491 		if ((sport->mgmt_functions) && (sport->mgmt_functions->get_all_handler)) {
1492 			sport->mgmt_functions->get_all_handler(textbuf, sport);
1493 		}
1494 	}
1495 	ocs_domain_unlock(domain);
1496 
1497 	ocs_mgmt_end_unnumbered_section(textbuf, "domain");
1498 
1499 }
1500 
1501 int
1502 ocs_mgmt_domain_set(char *parent, char *name, char *value, void *object)
1503 {
1504 	ocs_sport_t *sport;
1505 	ocs_domain_t *domain = (ocs_domain_t *)object;
1506 	char qualifier[80];
1507 	int retval = -1;
1508 
1509 	snprintf(qualifier, sizeof(qualifier), "%s/domain[%d]", parent, domain->instance_index);
1510 
1511 	/* If it doesn't start with my qualifier I don't know what to do with it */
1512 	if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
1513 		/* See if it's a value I can supply */
1514 
1515 		/* if (ocs_strcmp(unqualified_name, "display_name") == 0) {
1516 		} else */
1517 		{
1518 			/* If I didn't know the value of this status pass the request to each of my children */
1519 			ocs_domain_lock(domain);
1520 			ocs_list_foreach(&domain->sport_list, sport) {
1521 				if ((sport->mgmt_functions) && (sport->mgmt_functions->set_handler)) {
1522 					retval = sport->mgmt_functions->set_handler(qualifier, name, value, sport);
1523 				}
1524 
1525 				if (retval == 0) {
1526 					break;
1527 				}
1528 			}
1529 			ocs_domain_unlock(domain);
1530 		}
1531 	}
1532 
1533 	return retval;
1534 }
1535 
1536 int
1537 ocs_mgmt_domain_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length,
1538 			void *arg_out, uint32_t arg_out_length, void *object)
1539 {
1540 	ocs_sport_t *sport;
1541 	ocs_domain_t *domain = (ocs_domain_t *)object;
1542 	char qualifier[80];
1543 	int retval = -1;
1544 
1545 	snprintf(qualifier, sizeof(qualifier), "%s.domain%d", parent, domain->instance_index);
1546 
1547 	/* If it doesn't start with my qualifier I don't know what to do with it */
1548 	if (ocs_strncmp(action, qualifier, strlen(qualifier)) == 0) {
1549 		{
1550 			/* If I didn't know how to do this action pass the request to each of my children */
1551 			ocs_domain_lock(domain);
1552 			ocs_list_foreach(&domain->sport_list, sport) {
1553 				if ((sport->mgmt_functions) && (sport->mgmt_functions->exec_handler)) {
1554 					retval = sport->mgmt_functions->exec_handler(qualifier, action, arg_in, arg_in_length, arg_out, arg_out_length, sport);
1555 				}
1556 
1557 				if (retval == 0) {
1558 					break;
1559 				}
1560 			}
1561 			ocs_domain_unlock(domain);
1562 		}
1563 	}
1564 
1565 	return retval;
1566 }
1567