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: stable/11/sys/dev/ocs_fc/ocs_domain.c 331766 2018-03-30 15:28:25Z ken $
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
65void ocs_mgmt_domain_list(ocs_textbuf_t *textbuf, void *domain);
66void ocs_mgmt_domain_get_all(ocs_textbuf_t *textbuf, void *domain);
67int ocs_mgmt_domain_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *domain);
68int ocs_mgmt_domain_set(char *parent, char *name, char *value, void *domain);
69int 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
72static 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
95int32_t
96ocs_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
190ocs_domain_t *
191ocs_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
219ocs_domain_t *
220ocs_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
276void
277ocs_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
326void
327ocs_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
359void
360ocs_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
383ocs_domain_t *
384ocs_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
417static 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
452static 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
508void *
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
645void *
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
772void *
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
864void *
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
956void *
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
1040void *
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
1083void *
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
1152void *
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
1215void
1216ocs_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
1234void
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
1253void
1254ocs_domain_attach(ocs_domain_t *domain, uint32_t s_id)
1255{
1256	__ocs_domain_attach_internal(domain, s_id);
1257}
1258
1259int
1260ocs_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
1299uint64_t
1300ocs_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
1326int
1327ocs_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
1371void
1372ocs_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
1407int
1408ocs_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
1481void
1482ocs_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
1515int
1516ocs_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
1553int
1554ocs_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