freebsd-dev/sys/dev/ocs_fc/ocs_domain.c
Kenneth D. Merry ef270ab1b6 Bring in the Broadcom/Emulex Fibre Channel driver, ocs_fc(4).
The ocs_fc(4) driver supports the following hardware:

Emulex 16/8G FC GEN 5 HBAS
	LPe15004 FC Host Bus Adapters
	LPe160XX FC Host Bus Adapters

Emulex 32/16G FC GEN 6 HBAS
	LPe3100X FC Host Bus Adapters
	LPe3200X FC Host Bus Adapters

The driver supports target and initiator mode, and also supports FC-Tape.

Note that the driver only currently works on little endian platforms.  It
is only included in the module build for amd64 and i386, and in GENERIC
on amd64 only.

Submitted by:	Ram Kishore Vegesna <ram.vegesna@broadcom.com>
Reviewed by:	mav
MFC after:	5 days
Relnotes:	yes
Sponsored by:	Broadcom
Differential Revision:	https://reviews.freebsd.org/D11423
2018-03-30 15:28:25 +00:00

1586 lines
43 KiB
C

/*-
* Copyright (c) 2017 Broadcom. All rights reserved.
* The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
/**
* @file
* Handles the domain object callback from the HW.
*/
/*!
@defgroup domain_sm Domain State Machine: States
*/
#include "ocs.h"
#include "ocs_fabric.h"
#include "ocs_device.h"
#define domain_sm_trace(domain) \
do { \
if (OCS_LOG_ENABLE_DOMAIN_SM_TRACE(domain->ocs)) \
ocs_log_info(domain->ocs, "[domain] %-20s %-20s\n", __func__, ocs_sm_event_name(evt)); \
} while (0)
#define domain_trace(domain, fmt, ...) \
do { \
if (OCS_LOG_ENABLE_DOMAIN_SM_TRACE(domain ? domain->ocs : NULL)) \
ocs_log_info(domain ? domain->ocs : NULL, fmt, ##__VA_ARGS__); \
} while (0)
#define domain_printf(domain, fmt, ...) \
do { \
ocs_log_info(domain ? domain->ocs : NULL, fmt, ##__VA_ARGS__); \
} while (0)
void ocs_mgmt_domain_list(ocs_textbuf_t *textbuf, void *domain);
void ocs_mgmt_domain_get_all(ocs_textbuf_t *textbuf, void *domain);
int ocs_mgmt_domain_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *domain);
int ocs_mgmt_domain_set(char *parent, char *name, char *value, void *domain);
int ocs_mgmt_domain_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length,
void *arg_out, uint32_t arg_out_length, void *domain);
static ocs_mgmt_functions_t domain_mgmt_functions = {
.get_list_handler = ocs_mgmt_domain_list,
.get_handler = ocs_mgmt_domain_get,
.get_all_handler = ocs_mgmt_domain_get_all,
.set_handler = ocs_mgmt_domain_set,
.exec_handler = ocs_mgmt_domain_exec,
};
/**
* @brief Accept domain callback events from the HW.
*
* <h3 class="desc">Description</h3>
* HW calls this function with various domain-related events.
*
* @param arg Application-specified argument.
* @param event Domain event.
* @param data Event specific data.
*
* @return Returns 0 on success; or a negative error value on failure.
*/
int32_t
ocs_domain_cb(void *arg, ocs_hw_domain_event_e event, void *data)
{
ocs_t *ocs = arg;
ocs_domain_t *domain = NULL;
int32_t rc = 0;
ocs_assert(data, -1);
if (event != OCS_HW_DOMAIN_FOUND) {
domain = data;
}
switch (event) {
case OCS_HW_DOMAIN_FOUND: {
uint64_t fcf_wwn = 0;
ocs_domain_record_t *drec = data;
ocs_assert(drec, -1);
/* extract the fcf_wwn */
fcf_wwn = ocs_be64toh(*((uint64_t*)drec->wwn));
/* lookup domain, or allocate a new one if one doesn't exist already */
domain = ocs_domain_find(ocs, fcf_wwn);
if (domain == NULL) {
domain = ocs_domain_alloc(ocs, fcf_wwn);
if (domain == NULL) {
ocs_log_err(ocs, "ocs_domain_alloc() failed\n");
rc = -1;
break;
}
ocs_sm_transition(&domain->drvsm, __ocs_domain_init, NULL);
}
ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FOUND, drec);
break;
}
case OCS_HW_DOMAIN_LOST:
domain_trace(domain, "OCS_HW_DOMAIN_LOST:\n");
ocs_domain_hold_frames(domain);
ocs_domain_post_event(domain, OCS_EVT_DOMAIN_LOST, NULL);
break;
case OCS_HW_DOMAIN_ALLOC_OK: {
domain_trace(domain, "OCS_HW_DOMAIN_ALLOC_OK:\n");
domain->instance_index = 0;
ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ALLOC_OK, NULL);
break;
}
case OCS_HW_DOMAIN_ALLOC_FAIL:
domain_trace(domain, "OCS_HW_DOMAIN_ALLOC_FAIL:\n");
ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ALLOC_FAIL, NULL);
break;
case OCS_HW_DOMAIN_ATTACH_OK:
domain_trace(domain, "OCS_HW_DOMAIN_ATTACH_OK:\n");
ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ATTACH_OK, NULL);
break;
case OCS_HW_DOMAIN_ATTACH_FAIL:
domain_trace(domain, "OCS_HW_DOMAIN_ATTACH_FAIL:\n");
ocs_domain_post_event(domain, OCS_EVT_DOMAIN_ATTACH_FAIL, NULL);
break;
case OCS_HW_DOMAIN_FREE_OK:
domain_trace(domain, "OCS_HW_DOMAIN_FREE_OK:\n");
ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FREE_OK, NULL);
break;
case OCS_HW_DOMAIN_FREE_FAIL:
domain_trace(domain, "OCS_HW_DOMAIN_FREE_FAIL:\n");
ocs_domain_post_event(domain, OCS_EVT_DOMAIN_FREE_FAIL, NULL);
break;
default:
ocs_log_warn(ocs, "unsupported event %#x\n", event);
}
return rc;
}
/**
* @brief Find the domain, given its FCF_WWN.
*
* <h3 class="desc">Description</h3>
* Search the domain_list to find a matching domain object.
*
* @param ocs Pointer to the OCS device.
* @param fcf_wwn FCF WWN to find.
*
* @return Returns the pointer to the domain if found; or NULL otherwise.
*/
ocs_domain_t *
ocs_domain_find(ocs_t *ocs, uint64_t fcf_wwn)
{
ocs_domain_t *domain = NULL;
/* Check to see if this domain is already allocated */
ocs_device_lock(ocs);
ocs_list_foreach(&ocs->domain_list, domain) {
if (fcf_wwn == domain->fcf_wwn) {
break;
}
}
ocs_device_unlock(ocs);
return domain;
}
/**
* @brief Allocate a domain object.
*
* <h3 class="desc">Description</h3>
* A domain object is allocated and initialized. It is associated with the
* \c ocs argument.
*
* @param ocs Pointer to the OCS device.
* @param fcf_wwn FCF WWN of the domain.
*
* @return Returns a pointer to the ocs_domain_t object; or NULL.
*/
ocs_domain_t *
ocs_domain_alloc(ocs_t *ocs, uint64_t fcf_wwn)
{
ocs_domain_t *domain;
ocs_assert(ocs, NULL);
domain = ocs_malloc(ocs, sizeof(*domain), OCS_M_NOWAIT | OCS_M_ZERO);
if (domain) {
domain->ocs = ocs;
domain->instance_index = ocs->domain_instance_count++;
domain->drvsm.app = domain;
ocs_domain_lock_init(domain);
ocs_lock_init(ocs, &domain->lookup_lock, "Domain lookup[%d]", domain->instance_index);
/* Allocate a sparse vector for sport FC_ID's */
domain->lookup = spv_new(ocs);
if (domain->lookup == NULL) {
ocs_log_err(ocs, "spv_new() failed\n");
ocs_free(ocs, domain, sizeof(*domain));
return NULL;
}
ocs_list_init(&domain->sport_list, ocs_sport_t, link);
domain->fcf_wwn = fcf_wwn;
ocs_log_debug(ocs, "Domain allocated: wwn %016" PRIX64 "\n", domain->fcf_wwn);
domain->femul_enable = (ocs->ctrlmask & OCS_CTRLMASK_ENABLE_FABRIC_EMULATION) != 0;
ocs_device_lock(ocs);
/* if this is the first domain, then assign it as the "root" domain */
if (ocs_list_empty(&ocs->domain_list)) {
ocs->domain = domain;
}
ocs_list_add_tail(&ocs->domain_list, domain);
ocs_device_unlock(ocs);
domain->mgmt_functions = &domain_mgmt_functions;
} else {
ocs_log_err(ocs, "domain allocation failed\n");
}
return domain;
}
/**
* @brief Free a domain object.
*
* <h3 class="desc">Description</h3>
* The domain object is freed.
*
* @param domain Domain object to free.
*
* @return None.
*/
void
ocs_domain_free(ocs_domain_t *domain)
{
ocs_t *ocs;
ocs_assert(domain);
ocs_assert(domain->ocs);
/* Hold frames to clear the domain pointer from the xport lookup */
ocs_domain_hold_frames(domain);
ocs = domain->ocs;
ocs_log_debug(ocs, "Domain free: wwn %016" PRIX64 "\n", domain->fcf_wwn);
spv_del(domain->lookup);
domain->lookup = NULL;
ocs_device_lock(ocs);
ocs_list_remove(&ocs->domain_list, domain);
if (domain == ocs->domain) {
/* set global domain to the new head */
ocs->domain = ocs_list_get_head(&ocs->domain_list);
if (ocs->domain) {
ocs_log_debug(ocs, "setting new domain, old=%p new=%p\n",
domain, ocs->domain);
}
}
if (ocs_list_empty(&ocs->domain_list) && ocs->domain_list_empty_cb ) {
(*ocs->domain_list_empty_cb)(ocs, ocs->domain_list_empty_cb_arg);
}
ocs_device_unlock(ocs);
ocs_lock_free(&domain->lookup_lock);
ocs_free(ocs, domain, sizeof(*domain));
}
/**
* @brief Free memory resources of a domain object.
*
* <h3 class="desc">Description</h3>
* After the domain object is freed, its child objects are also freed.
*
* @param domain Pointer to a domain object.
*
* @return None.
*/
void
ocs_domain_force_free(ocs_domain_t *domain)
{
ocs_sport_t *sport;
ocs_sport_t *next;
/* Shutdown domain sm */
ocs_sm_disable(&domain->drvsm);
ocs_scsi_notify_domain_force_free(domain);
ocs_domain_lock(domain);
ocs_list_foreach_safe(&domain->sport_list, sport, next) {
ocs_sport_force_free(sport);
}
ocs_domain_unlock(domain);
ocs_hw_domain_force_free(&domain->ocs->hw, domain);
ocs_domain_free(domain);
}
/**
* @brief Register a callback when the domain_list goes empty.
*
* <h3 class="desc">Description</h3>
* A function callback may be registered when the domain_list goes empty.
*
* @param ocs Pointer to a device object.
* @param callback Callback function.
* @param arg Callback argument.
*
* @return None.
*/
void
ocs_register_domain_list_empty_cb(ocs_t *ocs, void (*callback)(ocs_t *ocs, void *arg), void *arg)
{
ocs_device_lock(ocs);
ocs->domain_list_empty_cb = callback;
ocs->domain_list_empty_cb_arg = arg;
if (ocs_list_empty(&ocs->domain_list) && callback) {
(*callback)(ocs, arg);
}
ocs_device_unlock(ocs);
}
/**
* @brief Return a pointer to the domain, given the instance index.
*
* <h3 class="desc">Description</h3>
* A pointer to the domain context, given by the index, is returned.
*
* @param ocs Pointer to the driver instance context.
* @param index Instance index.
*
* @return Returns a pointer to the domain; or NULL.
*/
ocs_domain_t *
ocs_domain_get_instance(ocs_t *ocs, uint32_t index)
{
ocs_domain_t *domain = NULL;
if (index >= OCS_MAX_DOMAINS) {
ocs_log_err(ocs, "invalid index: %d\n", index);
return NULL;
}
ocs_device_lock(ocs);
ocs_list_foreach(&ocs->domain_list, domain) {
if (domain->instance_index == index) {
break;
}
}
ocs_device_unlock(ocs);
return domain;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Common event handler.
*
* <h3 class="desc">Description</h3>
* Common/shared events are handled here for the domain state machine.
*
* @param funcname Function name text.
* @param ctx Domain state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
static void *
__ocs_domain_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_domain_t *domain = ctx->app;
switch(evt) {
case OCS_EVT_ENTER:
case OCS_EVT_REENTER:
case OCS_EVT_EXIT:
case OCS_EVT_ALL_CHILD_NODES_FREE:
/* this can arise if an FLOGI fails on the SPORT, and the SPORT is shutdown */
break;
default:
ocs_log_warn(domain->ocs, "%-20s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
break;
}
return NULL;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Common shutdown.
*
* <h3 class="desc">Description</h3>
* Handles common shutdown events.
*
* @param funcname Function name text.
* @param ctx Remote node state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
static void *
__ocs_domain_common_shutdown(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_domain_t *domain = ctx->app;
switch(evt) {
case OCS_EVT_ENTER:
case OCS_EVT_REENTER:
case OCS_EVT_EXIT:
break;
case OCS_EVT_DOMAIN_FOUND:
ocs_assert(arg, NULL);
/* sm: / save drec, mark domain_found_pending */
ocs_memcpy(&domain->pending_drec, arg, sizeof(domain->pending_drec));
domain->domain_found_pending = TRUE;
break;
case OCS_EVT_DOMAIN_LOST:
/* clear drec available
* sm: unmark domain_found_pending */
domain->domain_found_pending = FALSE;
break;
default:
ocs_log_warn(domain->ocs, "%-20s %-20s not handled\n", funcname, ocs_sm_event_name(evt));
break;
}
return NULL;
}
#define std_domain_state_decl(...) \
ocs_domain_t *domain = NULL; \
ocs_t *ocs = NULL; \
\
ocs_assert(ctx, NULL); \
ocs_assert(ctx->app, NULL); \
domain = ctx->app; \
ocs_assert(domain->ocs, NULL); \
ocs = domain->ocs; \
ocs_assert(ocs->xport, NULL);
/**
* @ingroup domain_sm
* @brief Domain state machine: Initial state.
*
* <h3 class="desc">Description</h3>
* The initial state for a domain. Each domain is initialized to
* this state at start of day (SOD).
*
* @param ctx Domain state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
void *
__ocs_domain_init(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_domain_state_decl();
domain_sm_trace(domain);
switch(evt) {
case OCS_EVT_ENTER:
domain->attached = 0;
break;
case OCS_EVT_DOMAIN_FOUND: {
int32_t vlan = 0;
uint32_t i;
ocs_domain_record_t *drec = arg;
ocs_sport_t *sport;
uint64_t my_wwnn = ocs->xport->req_wwnn;
uint64_t my_wwpn = ocs->xport->req_wwpn;
uint64_t be_wwpn;
/* For now, user must specify both port name and node name, or we let firmware
* pick both (same as for vports).
* TODO: do we want to allow setting only port name or only node name?
*/
if ((my_wwpn == 0) || (my_wwnn == 0)) {
ocs_log_debug(ocs, "using default hardware WWN configuration \n");
my_wwpn = ocs_get_wwn(&ocs->hw, OCS_HW_WWN_PORT);
my_wwnn = ocs_get_wwn(&ocs->hw, OCS_HW_WWN_NODE);
}
ocs_log_debug(ocs, "Creating base sport using WWPN %016" PRIx64 " WWNN %016" PRIx64 "\n",
my_wwpn, my_wwnn);
/* Allocate a sport and transition to __ocs_sport_allocated */
sport = ocs_sport_alloc(domain, my_wwpn, my_wwnn, UINT32_MAX, ocs->enable_ini, ocs->enable_tgt);
if (sport == NULL) {
ocs_log_err(ocs, "ocs_sport_alloc() failed\n");
break;
}
ocs_sm_transition(&sport->sm, __ocs_sport_allocated, NULL);
/* If domain is ethernet, then fetch the vlan id value */
if (drec->is_ethernet) {
vlan = ocs_bitmap_search((void *)drec->map.vlan, TRUE, 512 * 8);
if (vlan < 0) {
ocs_log_err(ocs, "no VLAN id available (FCF=%d)\n",
drec->index);
break;
}
}
be_wwpn = ocs_htobe64(sport->wwpn);
/* allocate ocs_sli_port_t object for local port
* Note: drec->fc_id is ALPA from read_topology only if loop
*/
if (ocs_hw_port_alloc(&ocs->hw, sport, NULL, (uint8_t *)&be_wwpn)) {
ocs_log_err(ocs, "Can't allocate port\n");
ocs_sport_free(sport);
break;
}
/* initialize domain object */
domain->is_loop = drec->is_loop;
domain->is_fc = drec->is_fc;
/*
* If the loop position map includes ALPA == 0, then we are in a public loop (NL_PORT)
* Note that the first element of the loopmap[] contains the count of elements, and if
* ALPA == 0 is present, it will occupy the first location after the count.
*/
domain->is_nlport = drec->map.loop[1] == 0x00;
if (domain->is_loop) {
ocs_log_debug(ocs, "%s fc_id=%#x speed=%d\n",
drec->is_loop ? (domain->is_nlport ? "public-loop" : "loop") : "other",
drec->fc_id, drec->speed);
sport->fc_id = drec->fc_id;
sport->topology = OCS_SPORT_TOPOLOGY_LOOP;
ocs_snprintf(sport->display_name, sizeof(sport->display_name), "s%06x", drec->fc_id);
if (ocs->enable_ini) {
uint32_t count = drec->map.loop[0];
ocs_log_debug(ocs, "%d position map entries\n", count);
for (i = 1; i <= count; i++) {
if (drec->map.loop[i] != drec->fc_id) {
ocs_node_t *node;
ocs_log_debug(ocs, "%#x -> %#x\n",
drec->fc_id, drec->map.loop[i]);
node = ocs_node_alloc(sport, drec->map.loop[i], FALSE, TRUE);
if (node == NULL) {
ocs_log_err(ocs, "ocs_node_alloc() failed\n");
break;
}
ocs_node_transition(node, __ocs_d_wait_loop, NULL);
}
}
}
}
/* Initiate HW domain alloc */
if (ocs_hw_domain_alloc(&ocs->hw, domain, drec->index, vlan)) {
ocs_log_err(ocs, "Failed to initiate HW domain allocation\n");
break;
}
ocs_sm_transition(ctx, __ocs_domain_wait_alloc, arg);
break;
}
default:
__ocs_domain_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Wait for the domain allocation to complete.
*
* <h3 class="desc">Description</h3>
* Waits for the domain state to be allocated. After the HW domain
* allocation process has been initiated, this state waits for
* that process to complete (i.e. a domain-alloc-ok event).
*
* @param ctx Domain state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
void *
__ocs_domain_wait_alloc(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_sport_t *sport;
std_domain_state_decl();
domain_sm_trace(domain);
switch(evt) {
case OCS_EVT_DOMAIN_ALLOC_OK: {
char prop_buf[32];
uint64_t wwn_bump = 0;
fc_plogi_payload_t *sp;
if (ocs_get_property("wwn_bump", prop_buf, sizeof(prop_buf)) == 0) {
wwn_bump = ocs_strtoull(prop_buf, 0, 0);
}
sport = domain->sport;
ocs_assert(sport, NULL);
sp = (fc_plogi_payload_t*) sport->service_params;
/* Save the domain service parameters */
ocs_memcpy(domain->service_params + 4, domain->dma.virt, sizeof(fc_plogi_payload_t) - 4);
ocs_memcpy(sport->service_params + 4, domain->dma.virt, sizeof(fc_plogi_payload_t) - 4);
/* If we're in fabric emulation mode, the flogi service parameters have not been setup yet,
* so we need some reasonable BB credit value
*/
if (domain->femul_enable) {
ocs_memcpy(domain->flogi_service_params + 4, domain->service_params + 4, sizeof(fc_plogi_payload_t) - 4);
}
/* Update the sport's service parameters, user might have specified non-default names */
sp->port_name_hi = ocs_htobe32((uint32_t) (sport->wwpn >> 32ll));
sp->port_name_lo = ocs_htobe32((uint32_t) sport->wwpn);
sp->node_name_hi = ocs_htobe32((uint32_t) (sport->wwnn >> 32ll));
sp->node_name_lo = ocs_htobe32((uint32_t) sport->wwnn);
if (wwn_bump) {
sp->port_name_lo = ocs_htobe32(ocs_be32toh(sp->port_name_lo) ^ ((uint32_t)(wwn_bump)));
sp->port_name_hi = ocs_htobe32(ocs_be32toh(sp->port_name_hi) ^ ((uint32_t)(wwn_bump >> 32)));
sp->node_name_lo = ocs_htobe32(ocs_be32toh(sp->node_name_lo) ^ ((uint32_t)(wwn_bump)));
sp->node_name_hi = ocs_htobe32(ocs_be32toh(sp->node_name_hi) ^ ((uint32_t)(wwn_bump >> 32)));
ocs_log_info(ocs, "Overriding WWN\n");
}
/* Take the loop topology path, unless we are an NL_PORT (public loop) */
if (domain->is_loop && !domain->is_nlport) {
/*
* For loop, we already have our FC ID and don't need fabric login.
* Transition to the allocated state and post an event to attach to
* the domain. Note that this breaks the normal action/transition
* pattern here to avoid a race with the domain attach callback.
*/
/* sm: is_loop / domain_attach */
ocs_sm_transition(ctx, __ocs_domain_allocated, NULL);
__ocs_domain_attach_internal(domain, sport->fc_id);
break;
} else {
ocs_node_t *node;
/* alloc fabric node, send FLOGI */
node = ocs_node_find(sport, FC_ADDR_FABRIC);
if (node) {
ocs_log_err(ocs, "Hmmmm ... Fabric Controller node already exists\n");
break;
}
node = ocs_node_alloc(sport, FC_ADDR_FABRIC, FALSE, FALSE);
if (!node) {
ocs_log_err(ocs, "Error: ocs_node_alloc() failed\n");
} else {
if (ocs->nodedb_mask & OCS_NODEDB_PAUSE_FABRIC_LOGIN) {
ocs_node_pause(node, __ocs_fabric_init);
} else {
ocs_node_transition(node, __ocs_fabric_init, NULL);
}
}
/* Accept frames */
domain->req_accept_frames = 1;
}
/* sm: start fabric logins */
ocs_sm_transition(ctx, __ocs_domain_allocated, NULL);
break;
}
case OCS_EVT_DOMAIN_ALLOC_FAIL:
/* TODO: hw/device reset */
ocs_log_err(ocs, "%s recv'd waiting for DOMAIN_ALLOC_OK; shutting down domain\n",
ocs_sm_event_name(evt));
domain->req_domain_free = 1;
break;
case OCS_EVT_DOMAIN_FOUND:
/* Should not happen */
ocs_assert(evt, NULL);
break;
case OCS_EVT_DOMAIN_LOST:
ocs_log_debug(ocs, "%s received while waiting for ocs_hw_domain_alloc() to complete\n", ocs_sm_event_name(evt));
ocs_sm_transition(ctx, __ocs_domain_wait_domain_lost, NULL);
break;
default:
__ocs_domain_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Wait for the domain attach request.
*
* <h3 class="desc">Description</h3>
* In this state, the domain has been allocated and is waiting for a domain attach request.
* The attach request comes from a node instance completing the fabric login,
* or from a point-to-point negotiation and login.
*
* @param ctx Remote node state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
void *
__ocs_domain_allocated(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
int32_t rc = 0;
std_domain_state_decl();
domain_sm_trace(domain);
switch(evt) {
case OCS_EVT_DOMAIN_REQ_ATTACH: {
uint32_t fc_id;
ocs_assert(arg, NULL);
fc_id = *((uint32_t*)arg);
ocs_log_debug(ocs, "Requesting hw domain attach fc_id x%x\n", fc_id);
/* Update sport lookup */
ocs_lock(&domain->lookup_lock);
spv_set(domain->lookup, fc_id, domain->sport);
ocs_unlock(&domain->lookup_lock);
/* Update display name for the sport */
ocs_node_fcid_display(fc_id, domain->sport->display_name, sizeof(domain->sport->display_name));
/* Issue domain attach call */
rc = ocs_hw_domain_attach(&ocs->hw, domain, fc_id);
if (rc) {
ocs_log_err(ocs, "ocs_hw_domain_attach failed: %d\n", rc);
return NULL;
}
/* sm: / domain_attach */
ocs_sm_transition(ctx, __ocs_domain_wait_attach, NULL);
break;
}
case OCS_EVT_DOMAIN_FOUND:
/* Should not happen */
ocs_assert(evt, NULL);
break;
case OCS_EVT_DOMAIN_LOST: {
int32_t rc;
ocs_log_debug(ocs, "%s received while waiting for OCS_EVT_DOMAIN_REQ_ATTACH\n",
ocs_sm_event_name(evt));
ocs_domain_lock(domain);
if (!ocs_list_empty(&domain->sport_list)) {
/* if there are sports, transition to wait state and
* send shutdown to each sport */
ocs_sport_t *sport = NULL;
ocs_sport_t *sport_next = NULL;
ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
}
ocs_domain_unlock(domain);
} else {
ocs_domain_unlock(domain);
/* no sports exist, free domain */
ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
rc = ocs_hw_domain_free(&ocs->hw, domain);
if (rc) {
ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
/* TODO: hw/device reset needed */
}
}
break;
}
default:
__ocs_domain_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Wait for the HW domain attach to complete.
*
* <h3 class="desc">Description</h3>
* Waits for the HW domain attach to complete. Forwards attach ok event to the
* fabric node state machine.
*
* @param ctx Remote node state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
void *
__ocs_domain_wait_attach(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_domain_state_decl();
domain_sm_trace(domain);
switch(evt) {
case OCS_EVT_DOMAIN_ATTACH_OK: {
ocs_node_t *node = NULL;
ocs_node_t *next_node = NULL;
ocs_sport_t *sport;
ocs_sport_t *next_sport;
/* Mark as attached */
domain->attached = 1;
/* Register with SCSI API */
if (ocs->enable_tgt)
ocs_scsi_tgt_new_domain(domain);
if (ocs->enable_ini)
ocs_scsi_ini_new_domain(domain);
/* Transition to ready */
/* sm: / forward event to all sports and nodes */
ocs_sm_transition(ctx, __ocs_domain_ready, NULL);
/* We have an FCFI, so we can accept frames */
domain->req_accept_frames = 1;
/* Set domain notify pending state to avoid duplicate domain event post */
domain->domain_notify_pend = 1;
/* Notify all nodes that the domain attach request has completed
* Note: sport will have already received notification of sport attached
* as a result of the HW's port attach.
*/
ocs_domain_lock(domain);
ocs_list_foreach_safe(&domain->sport_list, sport, next_sport) {
ocs_sport_lock(sport);
ocs_list_foreach_safe(&sport->node_list, node, next_node) {
ocs_node_post_event(node, OCS_EVT_DOMAIN_ATTACH_OK, NULL);
}
ocs_sport_unlock(sport);
}
ocs_domain_unlock(domain);
domain->domain_notify_pend = 0;
break;
}
case OCS_EVT_DOMAIN_ATTACH_FAIL:
ocs_log_debug(ocs, "%s received while waiting for hw attach to complete\n", ocs_sm_event_name(evt));
/* TODO: hw/device reset */
break;
case OCS_EVT_DOMAIN_FOUND:
/* Should not happen */
ocs_assert(evt, NULL);
break;
case OCS_EVT_DOMAIN_LOST:
/* Domain lost while waiting for an attach to complete, go to a state that waits for
* the domain attach to complete, then handle domain lost
*/
ocs_sm_transition(ctx, __ocs_domain_wait_domain_lost, NULL);
break;
case OCS_EVT_DOMAIN_REQ_ATTACH:
/* In P2P we can get an attach request from the other FLOGI path, so drop this one */
break;
default:
__ocs_domain_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Ready state.
*
* <h3 class="desc">Description</h3>
* This is a domain ready state. It waits for a domain-lost event, and initiates shutdown.
*
* @param ctx Remote node state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
void *
__ocs_domain_ready(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_domain_state_decl();
domain_sm_trace(domain);
switch(evt) {
case OCS_EVT_ENTER: {
/* start any pending vports */
if (ocs_vport_start(domain)) {
ocs_log_debug(domain->ocs, "ocs_vport_start() did not start all vports\n");
}
break;
}
case OCS_EVT_DOMAIN_LOST: {
int32_t rc;
ocs_domain_lock(domain);
if (!ocs_list_empty(&domain->sport_list)) {
/* if there are sports, transition to wait state and send
* shutdown to each sport */
ocs_sport_t *sport = NULL;
ocs_sport_t *sport_next = NULL;
ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
}
ocs_domain_unlock(domain);
} else {
ocs_domain_unlock(domain);
/* no sports exist, free domain */
ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
rc = ocs_hw_domain_free(&ocs->hw, domain);
if (rc) {
ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
/* TODO: hw/device reset needed */
}
}
break;
}
case OCS_EVT_DOMAIN_FOUND:
/* Should not happen */
ocs_assert(evt, NULL);
break;
case OCS_EVT_DOMAIN_REQ_ATTACH: {
/* can happen during p2p */
uint32_t fc_id;
ocs_assert(arg, NULL);
fc_id = *((uint32_t*)arg);
/* Assume that the domain is attached */
ocs_assert(domain->attached, NULL);
/* Verify that the requested FC_ID is the same as the one we're working with */
ocs_assert(domain->sport->fc_id == fc_id, NULL);
break;
}
default:
__ocs_domain_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Wait for nodes to free prior to the domain shutdown.
*
* <h3 class="desc">Description</h3>
* All nodes are freed, and ready for a domain shutdown.
*
* @param ctx Remote node sm context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
void *
__ocs_domain_wait_sports_free(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_domain_state_decl();
domain_sm_trace(domain);
switch(evt) {
case OCS_EVT_ALL_CHILD_NODES_FREE: {
int32_t rc;
/* sm: ocs_hw_domain_free */
ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
/* Request ocs_hw_domain_free and wait for completion */
rc = ocs_hw_domain_free(&ocs->hw, domain);
if (rc) {
ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
}
break;
}
default:
__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Complete the domain shutdown.
*
* <h3 class="desc">Description</h3>
* Waits for a HW domain free to complete.
*
* @param ctx Remote node state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
void *
__ocs_domain_wait_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_domain_state_decl();
domain_sm_trace(domain);
switch(evt) {
case OCS_EVT_DOMAIN_FREE_OK: {
if (ocs->enable_ini)
ocs_scsi_ini_del_domain(domain);
if (ocs->enable_tgt)
ocs_scsi_tgt_del_domain(domain);
/* sm: domain_free */
if (domain->domain_found_pending) {
/* save fcf_wwn and drec from this domain, free current domain and allocate
* a new one with the same fcf_wwn
* TODO: could use a SLI-4 "re-register VPI" operation here
*/
uint64_t fcf_wwn = domain->fcf_wwn;
ocs_domain_record_t drec = domain->pending_drec;
ocs_log_debug(ocs, "Reallocating domain\n");
domain->req_domain_free = 1;
domain = ocs_domain_alloc(ocs, fcf_wwn);
if (domain == NULL) {
ocs_log_err(ocs, "ocs_domain_alloc() failed\n");
/* TODO: hw/device reset needed */
return NULL;
}
/*
* got a new domain; at this point, there are at least two domains
* once the req_domain_free flag is processed, the associated domain
* will be removed.
*/
ocs_sm_transition(&domain->drvsm, __ocs_domain_init, NULL);
ocs_sm_post_event(&domain->drvsm, OCS_EVT_DOMAIN_FOUND, &drec);
} else {
domain->req_domain_free = 1;
}
break;
}
default:
__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
/**
* @ingroup domain_sm
* @brief Domain state machine: Wait for the domain alloc/attach completion
* after receiving a domain lost.
*
* <h3 class="desc">Description</h3>
* This state is entered when receiving a domain lost while waiting for a domain alloc
* or a domain attach to complete.
*
* @param ctx Remote node state machine context.
* @param evt Event to process.
* @param arg Per event optional argument.
*
* @return Returns NULL.
*/
void *
__ocs_domain_wait_domain_lost(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_domain_state_decl();
domain_sm_trace(domain);
switch(evt) {
case OCS_EVT_DOMAIN_ALLOC_OK:
case OCS_EVT_DOMAIN_ATTACH_OK: {
int32_t rc;
ocs_domain_lock(domain);
if (!ocs_list_empty(&domain->sport_list)) {
/* if there are sports, transition to wait state and send
* shutdown to each sport */
ocs_sport_t *sport = NULL;
ocs_sport_t *sport_next = NULL;
ocs_sm_transition(ctx, __ocs_domain_wait_sports_free, NULL);
ocs_list_foreach_safe(&domain->sport_list, sport, sport_next) {
ocs_sm_post_event(&sport->sm, OCS_EVT_SHUTDOWN, NULL);
}
ocs_domain_unlock(domain);
} else {
ocs_domain_unlock(domain);
/* no sports exist, free domain */
ocs_sm_transition(ctx, __ocs_domain_wait_shutdown, NULL);
rc = ocs_hw_domain_free(&ocs->hw, domain);
if (rc) {
ocs_log_err(ocs, "ocs_hw_domain_free() failed: %d\n", rc);
/* TODO: hw/device reset needed */
}
}
break;
}
case OCS_EVT_DOMAIN_ALLOC_FAIL:
case OCS_EVT_DOMAIN_ATTACH_FAIL:
ocs_log_err(ocs, "[domain] %-20s: failed\n", ocs_sm_event_name(evt));
/* TODO: hw/device reset needed */
break;
default:
__ocs_domain_common_shutdown(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
/**
* @brief Save the port's service parameters.
*
* <h3 class="desc">Description</h3>
* Service parameters from the fabric FLOGI are saved in the domain's
* flogi_service_params array.
*
* @param domain Pointer to the domain.
* @param payload Service parameters to save.
*
* @return None.
*/
void
ocs_domain_save_sparms(ocs_domain_t *domain, void *payload)
{
ocs_memcpy(domain->flogi_service_params, payload, sizeof (fc_plogi_payload_t));
}
/**
* @brief Initiator domain attach. (internal call only)
*
* Assumes that the domain SM lock is already locked
*
* <h3 class="desc">Description</h3>
* The HW domain attach function is started.
*
* @param domain Pointer to the domain object.
* @param s_id FC_ID of which to register this domain.
*
* @return None.
*/
void
__ocs_domain_attach_internal(ocs_domain_t *domain, uint32_t s_id)
{
ocs_memcpy(domain->dma.virt, ((uint8_t*)domain->flogi_service_params)+4, sizeof (fc_plogi_payload_t) - 4);
(void)ocs_sm_post_event(&domain->drvsm, OCS_EVT_DOMAIN_REQ_ATTACH, &s_id);
}
/**
* @brief Initiator domain attach.
*
* <h3 class="desc">Description</h3>
* The HW domain attach function is started.
*
* @param domain Pointer to the domain object.
* @param s_id FC_ID of which to register this domain.
*
* @return None.
*/
void
ocs_domain_attach(ocs_domain_t *domain, uint32_t s_id)
{
__ocs_domain_attach_internal(domain, s_id);
}
int
ocs_domain_post_event(ocs_domain_t *domain, ocs_sm_event_t event, void *arg)
{
int rc;
int accept_frames;
int req_domain_free;
rc = ocs_sm_post_event(&domain->drvsm, event, arg);
req_domain_free = domain->req_domain_free;
domain->req_domain_free = 0;
accept_frames = domain->req_accept_frames;
domain->req_accept_frames = 0;
if (accept_frames) {
ocs_domain_accept_frames(domain);
}
if (req_domain_free) {
ocs_domain_free(domain);
}
return rc;
}
/**
* @brief Return the WWN as a uint64_t.
*
* <h3 class="desc">Description</h3>
* Calls the HW property function for the WWNN or WWPN, and returns the value
* as a uint64_t.
*
* @param hw Pointer to the HW object.
* @param prop HW property.
*
* @return Returns uint64_t request value.
*/
uint64_t
ocs_get_wwn(ocs_hw_t *hw, ocs_hw_property_e prop)
{
uint8_t *p = ocs_hw_get_ptr(hw, prop);
uint64_t value = 0;
if (p) {
uint32_t i;
for (i = 0; i < sizeof(value); i++) {
value = (value << 8) | p[i];
}
}
return value;
}
/**
* @brief Generate a domain ddump.
*
* <h3 class="desc">Description</h3>
* Generates a domain ddump.
*
* @param textbuf Pointer to the text buffer.
* @param domain Pointer to the domain context.
*
* @return Returns 0 on success, or a negative value on failure.
*/
int
ocs_ddump_domain(ocs_textbuf_t *textbuf, ocs_domain_t *domain)
{
ocs_sport_t *sport;
int retval = 0;
ocs_ddump_section(textbuf, "domain", domain->instance_index);
ocs_ddump_value(textbuf, "display_name", "%s", domain->display_name);
ocs_ddump_value(textbuf, "fcf", "%#x", domain->fcf);
ocs_ddump_value(textbuf, "fcf_indicator", "%#x", domain->fcf_indicator);
ocs_ddump_value(textbuf, "vlan_id", "%#x", domain->vlan_id);
ocs_ddump_value(textbuf, "indicator", "%#x", domain->indicator);
ocs_ddump_value(textbuf, "attached", "%d", domain->attached);
ocs_ddump_value(textbuf, "is_loop", "%d", domain->is_loop);
ocs_ddump_value(textbuf, "is_nlport", "%d", domain->is_nlport);
ocs_scsi_ini_ddump(textbuf, OCS_SCSI_DDUMP_DOMAIN, domain);
ocs_scsi_tgt_ddump(textbuf, OCS_SCSI_DDUMP_DOMAIN, domain);
ocs_display_sparams(NULL, "domain_sparms", 1, textbuf, domain->dma.virt);
if (ocs_domain_lock_try(domain) != TRUE) {
/* Didn't get the lock */
return -1;
}
ocs_list_foreach(&domain->sport_list, sport) {
retval = ocs_ddump_sport(textbuf, sport);
if (retval != 0) {
break;
}
}
#if defined(ENABLE_FABRIC_EMULATION)
ocs_ddump_ns(textbuf, domain->ocs_ns);
#endif
ocs_domain_unlock(domain);
ocs_ddump_endsection(textbuf, "domain", domain->instance_index);
return retval;
}
void
ocs_mgmt_domain_list(ocs_textbuf_t *textbuf, void *object)
{
ocs_sport_t *sport;
ocs_domain_t *domain = (ocs_domain_t *)object;
ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
/* Add my status values to textbuf */
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "fcf");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "fcf_indicator");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "vlan_id");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "indicator");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "attached");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "is_loop");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "display_name");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "num_sports");
#if defined(ENABLE_FABRIC_EMULATION)
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RW, "femul_enable");
#endif
if (ocs_domain_lock_try(domain) == TRUE) {
/* If we get here, then we are holding the domain lock */
ocs_list_foreach(&domain->sport_list, sport) {
if ((sport->mgmt_functions) && (sport->mgmt_functions->get_list_handler)) {
sport->mgmt_functions->get_list_handler(textbuf, sport);
}
}
ocs_domain_unlock(domain);
}
ocs_mgmt_end_section(textbuf, "domain", domain->instance_index);
}
int
ocs_mgmt_domain_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *object)
{
ocs_sport_t *sport;
ocs_domain_t *domain = (ocs_domain_t *)object;
char qualifier[80];
int retval = -1;
ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
snprintf(qualifier, sizeof(qualifier), "%s/domain[%d]", parent, domain->instance_index);
/* If it doesn't start with my qualifier I don't know what to do with it */
if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
char *unqualified_name = name + strlen(qualifier) +1;
/* See if it's a value I can supply */
if (ocs_strcmp(unqualified_name, "display_name") == 0) {
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "fcf") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf", "%#x", domain->fcf);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "fcf_indicator") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf_indicator", "%#x", domain->fcf_indicator);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "vlan_id") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "vlan_id", "%#x", domain->vlan_id);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "indicator") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "%#x", domain->indicator);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "attached") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "attached", domain->attached);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "is_loop") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_loop", domain->is_loop);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "is_nlport") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_nlport", domain->is_nlport);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "display_name") == 0) {
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
retval = 0;
#if defined(ENABLE_FABRIC_EMULATION)
} else if (ocs_strcmp(unqualified_name, "femul_enable") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "femul_enable", "%d", domain->femul_enable);
retval = 0;
#endif
} else if (ocs_strcmp(unqualified_name, "num_sports") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "num_sports", "%d", domain->sport_instance_count);
retval = 0;
} else {
/* If I didn't know the value of this status pass the request to each of my children */
ocs_domain_lock(domain);
ocs_list_foreach(&domain->sport_list, sport) {
if ((sport->mgmt_functions) && (sport->mgmt_functions->get_handler)) {
retval = sport->mgmt_functions->get_handler(textbuf, qualifier, name, sport);
}
if (retval == 0) {
break;
}
}
ocs_domain_unlock(domain);
}
}
ocs_mgmt_end_section(textbuf, "domain", domain->instance_index);
return retval;
}
void
ocs_mgmt_domain_get_all(ocs_textbuf_t *textbuf, void *object)
{
ocs_sport_t *sport;
ocs_domain_t *domain = (ocs_domain_t *)object;
ocs_mgmt_start_section(textbuf, "domain", domain->instance_index);
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", domain->display_name);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf", "%#x", domain->fcf);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fcf_indicator", "%#x", domain->fcf_indicator);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "vlan_id", "%#x", domain->vlan_id);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "%#x", domain->indicator);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "attached", domain->attached);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_loop", domain->is_loop);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "is_nlport", domain->is_nlport);
#if defined(ENABLE_FABRIC_EMULATION)
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "femul_enable", "%d", domain->femul_enable);
#endif
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "num_sports", "%d", domain->sport_instance_count);
ocs_domain_lock(domain);
ocs_list_foreach(&domain->sport_list, sport) {
if ((sport->mgmt_functions) && (sport->mgmt_functions->get_all_handler)) {
sport->mgmt_functions->get_all_handler(textbuf, sport);
}
}
ocs_domain_unlock(domain);
ocs_mgmt_end_unnumbered_section(textbuf, "domain");
}
int
ocs_mgmt_domain_set(char *parent, char *name, char *value, void *object)
{
ocs_sport_t *sport;
ocs_domain_t *domain = (ocs_domain_t *)object;
char qualifier[80];
int retval = -1;
snprintf(qualifier, sizeof(qualifier), "%s/domain[%d]", parent, domain->instance_index);
/* If it doesn't start with my qualifier I don't know what to do with it */
if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
/* See if it's a value I can supply */
/* if (ocs_strcmp(unqualified_name, "display_name") == 0) {
} else */
{
/* If I didn't know the value of this status pass the request to each of my children */
ocs_domain_lock(domain);
ocs_list_foreach(&domain->sport_list, sport) {
if ((sport->mgmt_functions) && (sport->mgmt_functions->set_handler)) {
retval = sport->mgmt_functions->set_handler(qualifier, name, value, sport);
}
if (retval == 0) {
break;
}
}
ocs_domain_unlock(domain);
}
}
return retval;
}
int
ocs_mgmt_domain_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length,
void *arg_out, uint32_t arg_out_length, void *object)
{
ocs_sport_t *sport;
ocs_domain_t *domain = (ocs_domain_t *)object;
char qualifier[80];
int retval = -1;
snprintf(qualifier, sizeof(qualifier), "%s.domain%d", parent, domain->instance_index);
/* If it doesn't start with my qualifier I don't know what to do with it */
if (ocs_strncmp(action, qualifier, strlen(qualifier)) == 0) {
{
/* If I didn't know how to do this action pass the request to each of my children */
ocs_domain_lock(domain);
ocs_list_foreach(&domain->sport_list, sport) {
if ((sport->mgmt_functions) && (sport->mgmt_functions->exec_handler)) {
retval = sport->mgmt_functions->exec_handler(qualifier, action, arg_in, arg_in_length, arg_out, arg_out_length, sport);
}
if (retval == 0) {
break;
}
}
ocs_domain_unlock(domain);
}
}
return retval;
}