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