#include "ocs.h"
#include "ocs_els.h"
#include "ocs_device.h"
#define SCSI_IOFMT "[%04x][i:%0*x t:%0*x h:%04x]"
#define SCSI_ITT_SIZE(ocs) ((ocs->ocs_xport == OCS_XPORT_FC) ? 4 : 8)
#define SCSI_IOFMT_ARGS(io) io->instance_index, SCSI_ITT_SIZE(io->ocs), io->init_task_tag, SCSI_ITT_SIZE(io->ocs), io->tgt_task_tag, io->hw_tag
#define scsi_io_printf(io, fmt, ...) ocs_log_debug(io->ocs, "[%s]" SCSI_IOFMT fmt, \
io->node->display_name, SCSI_IOFMT_ARGS(io), ##__VA_ARGS__)
void ocs_mgmt_node_list(ocs_textbuf_t *textbuf, void *node);
void ocs_mgmt_node_get_all(ocs_textbuf_t *textbuf, void *node);
int ocs_mgmt_node_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *node);
int ocs_mgmt_node_set(char *parent, char *name, char *value, void *node);
int ocs_mgmt_node_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length,
void *arg_out, uint32_t arg_out_length, void *node);
static ocs_mgmt_functions_t node_mgmt_functions = {
.get_list_handler = ocs_mgmt_node_list,
.get_handler = ocs_mgmt_node_get,
.get_all_handler = ocs_mgmt_node_get_all,
.set_handler = ocs_mgmt_node_set,
.exec_handler = ocs_mgmt_node_exec,
};
void
ocs_node_abort_all_els(ocs_node_t *node)
{
ocs_io_t *els;
ocs_io_t *els_next;
ocs_node_cb_t cbdata = {0};
ocs_node_hold_frames(node);
ocs_lock(&node->active_ios_lock);
ocs_list_foreach_safe(&node->els_io_active_list, els, els_next) {
ocs_log_debug(node->ocs, "[%s] initiate ELS abort %s\n", node->display_name, els->display_name);
ocs_unlock(&node->active_ios_lock);
cbdata.els = els;
ocs_els_post_event(els, OCS_EVT_ABORT_ELS, &cbdata);
ocs_lock(&node->active_ios_lock);
}
ocs_unlock(&node->active_ios_lock);
}
int32_t
ocs_remote_node_cb(void *arg, ocs_hw_remote_node_event_e event, void *data)
{
ocs_t *ocs = arg;
ocs_sm_event_t sm_event = OCS_EVT_LAST;
ocs_remote_node_t *rnode = data;
ocs_node_t *node = rnode->node;
switch (event) {
case OCS_HW_NODE_ATTACH_OK:
sm_event = OCS_EVT_NODE_ATTACH_OK;
break;
case OCS_HW_NODE_ATTACH_FAIL:
sm_event = OCS_EVT_NODE_ATTACH_FAIL;
break;
case OCS_HW_NODE_FREE_OK:
sm_event = OCS_EVT_NODE_FREE_OK;
break;
case OCS_HW_NODE_FREE_FAIL:
sm_event = OCS_EVT_NODE_FREE_FAIL;
break;
default:
ocs_log_test(ocs, "unhandled event %#x\n", event);
return -1;
}
if ((node->node_group != NULL) &&
((sm_event == OCS_EVT_NODE_ATTACH_OK) || (sm_event == OCS_EVT_NODE_ATTACH_FAIL))) {
ocs_node_t *n = NULL;
uint8_t attach_ok = sm_event == OCS_EVT_NODE_ATTACH_OK;
ocs_sport_lock(node->sport);
{
ocs_list_foreach(&node->sport->node_list, n) {
if (node == n) {
continue;
}
ocs_node_lock(n);
if ((!n->rnode.attached) && (node->node_group == n->node_group)) {
n->rnode.attached = attach_ok;
node_printf(n, "rpi[%d] deferred HLM node attach %s posted\n",
n->rnode.index, attach_ok ? "ok" : "fail");
ocs_node_post_event(n, sm_event, NULL);
}
ocs_node_unlock(n);
}
}
ocs_sport_unlock(node->sport);
}
ocs_node_post_event(node, sm_event, NULL);
return 0;
}
ocs_node_t *
ocs_node_find(ocs_sport_t *sport, uint32_t port_id)
{
ocs_node_t *node;
ocs_assert(sport->lookup, NULL);
ocs_sport_lock(sport);
node = spv_get(sport->lookup, port_id);
ocs_sport_unlock(sport);
return node;
}
ocs_node_t *
ocs_node_find_wwpn(ocs_sport_t *sport, uint64_t wwpn)
{
ocs_node_t *node = NULL;
ocs_assert(sport, NULL);
ocs_sport_lock(sport);
ocs_list_foreach(&sport->node_list, node) {
if (ocs_node_get_wwpn(node) == wwpn) {
ocs_sport_unlock(sport);
return node;
}
}
ocs_sport_unlock(sport);
return NULL;
}
int32_t
ocs_node_create_pool(ocs_t *ocs, uint32_t node_count)
{
ocs_xport_t *xport = ocs->xport;
uint32_t i;
ocs_node_t *node;
uint32_t max_sge;
uint32_t num_sgl;
uint64_t max_xfer_size;
int32_t rc;
xport->nodes_count = node_count;
xport->nodes = ocs_malloc(ocs, node_count * sizeof(ocs_node_t *), OCS_M_ZERO | OCS_M_NOWAIT);
if (xport->nodes == NULL) {
ocs_log_err(ocs, "node ptrs allocation failed");
return -1;
}
if (0 == ocs_hw_get(&ocs->hw, OCS_HW_MAX_SGE, &max_sge) &&
0 == ocs_hw_get(&ocs->hw, OCS_HW_N_SGL, &num_sgl)) {
max_xfer_size = (max_sge * (uint64_t)num_sgl);
} else {
max_xfer_size = 65536;
}
if (max_xfer_size > 65536)
max_xfer_size = 65536;
ocs_list_init(&xport->nodes_free_list, ocs_node_t, link);
for (i = 0; i < node_count; i ++) {
node = ocs_malloc(ocs, sizeof(ocs_node_t), OCS_M_ZERO | OCS_M_NOWAIT);
if (node == NULL) {
ocs_log_err(ocs, "node allocation failed");
goto error;
}
node->instance_index = i;
node->max_wr_xfer_size = max_xfer_size;
node->rnode.indicator = UINT32_MAX;
rc = ocs_dma_alloc(ocs, &node->sparm_dma_buf, 256, 16);
if (rc) {
ocs_free(ocs, node, sizeof(ocs_node_t));
ocs_log_err(ocs, "ocs_dma_alloc failed: %d\n", rc);
goto error;
}
xport->nodes[i] = node;
ocs_list_add_tail(&xport->nodes_free_list, node);
}
return 0;
error:
ocs_node_free_pool(ocs);
return -1;
}
void
ocs_node_free_pool(ocs_t *ocs)
{
ocs_xport_t *xport = ocs->xport;
ocs_node_t *node;
uint32_t i;
if (!xport->nodes)
return;
ocs_device_lock(ocs);
for (i = 0; i < xport->nodes_count; i ++) {
node = xport->nodes[i];
if (node) {
ocs_dma_free(ocs, &node->sparm_dma_buf);
ocs_free(ocs, node, sizeof(ocs_node_t));
}
xport->nodes[i] = NULL;
}
ocs_free(ocs, xport->nodes, (xport->nodes_count * sizeof(ocs_node_t *)));
ocs_device_unlock(ocs);
}
ocs_node_t *
ocs_node_get_instance(ocs_t *ocs, uint32_t index)
{
ocs_xport_t *xport = ocs->xport;
ocs_node_t *node = NULL;
if (index >= (xport->nodes_count)) {
ocs_log_test(ocs, "invalid index: %d\n", index);
return NULL;
}
node = xport->nodes[index];
return node->attached ? node : NULL;
}
ocs_node_t *
ocs_node_alloc(ocs_sport_t *sport, uint32_t port_id, uint8_t init, uint8_t targ)
{
int32_t rc;
ocs_node_t *node = NULL;
uint32_t instance_index;
uint32_t max_wr_xfer_size;
ocs_t *ocs = sport->ocs;
ocs_xport_t *xport = ocs->xport;
ocs_dma_t sparm_dma_buf;
ocs_assert(sport, NULL);
if (sport->shutting_down) {
ocs_log_debug(ocs, "node allocation when shutting down %06x", port_id);
return NULL;
}
ocs_device_lock(ocs);
node = ocs_list_remove_head(&xport->nodes_free_list);
ocs_device_unlock(ocs);
if (node == NULL) {
ocs_log_err(ocs, "node allocation failed %06x", port_id);
return NULL;
}
instance_index = node->instance_index;
max_wr_xfer_size = node->max_wr_xfer_size;
sparm_dma_buf = node->sparm_dma_buf;
ocs_memset(node, 0, sizeof(*node));
node->instance_index = instance_index;
node->max_wr_xfer_size = max_wr_xfer_size;
node->sparm_dma_buf = sparm_dma_buf;
node->rnode.indicator = UINT32_MAX;
node->sport = sport;
ocs_sport_lock(sport);
node->ocs = ocs;
node->init = init;
node->targ = targ;
rc = ocs_hw_node_alloc(&ocs->hw, &node->rnode, port_id, sport);
if (rc) {
ocs_log_err(ocs, "ocs_hw_node_alloc failed: %d\n", rc);
ocs_sport_unlock(sport);
ocs_device_lock(ocs);
ocs_list_add_tail(&xport->nodes_free_list, node);
ocs_device_unlock(ocs);
return NULL;
}
ocs_list_add_tail(&sport->node_list, node);
ocs_node_lock_init(node);
ocs_lock_init(ocs, &node->pend_frames_lock, "pend_frames_lock[%d]", node->instance_index);
ocs_list_init(&node->pend_frames, ocs_hw_sequence_t, link);
ocs_lock_init(ocs, &node->active_ios_lock, "active_ios[%d]", node->instance_index);
ocs_list_init(&node->active_ios, ocs_io_t, link);
ocs_list_init(&node->els_io_pend_list, ocs_io_t, link);
ocs_list_init(&node->els_io_active_list, ocs_io_t, link);
ocs_scsi_io_alloc_enable(node);
ocs_memset(node->sparm_dma_buf.virt, 0, node->sparm_dma_buf.size);
node->rnode.node = node;
node->sm.app = node;
node->evtdepth = 0;
ocs_node_update_display_name(node);
spv_set(sport->lookup, port_id, node);
ocs_sport_unlock(sport);
node->mgmt_functions = &node_mgmt_functions;
return node;
}
int32_t
ocs_node_free(ocs_node_t *node)
{
ocs_sport_t *sport;
ocs_t *ocs;
ocs_xport_t *xport;
ocs_hw_rtn_e rc = 0;
ocs_node_t *ns = NULL;
int post_all_free = FALSE;
ocs_assert(node, -1);
ocs_assert(node->sport, -1);
ocs_assert(node->ocs, -1);
sport = node->sport;
ocs_assert(sport, -1);
ocs = node->ocs;
ocs_assert(ocs->xport, -1);
xport = ocs->xport;
node_printf(node, "Free'd\n");
if(node->refound) {
ns = ocs_node_find(node->sport, FC_ADDR_NAMESERVER);
}
ocs_sport_lock(sport);
ocs_list_remove(&sport->node_list, node);
if (OCS_HW_RTN_IS_ERROR((rc = ocs_hw_node_free_resources(&ocs->hw, &node->rnode)))) {
ocs_log_test(ocs, "ocs_hw_node_free failed: %d\n", rc);
rc = -1;
}
if (ocs_timer_pending(&node->gidpt_delay_timer)) {
ocs_del_timer(&node->gidpt_delay_timer);
}
if (node->fcp2device) {
ocs_del_crn(node);
}
if (sport->lookup == NULL) {
ocs_log_test(node->ocs, "assertion failed: sport lookup is NULL\n");
ocs_sport_unlock(sport);
return -1;
}
spv_set(sport->lookup, node->rnode.fc_id, NULL);
if (ocs_list_empty(&sport->node_list)) {
post_all_free = TRUE;
}
ocs_sport_unlock(sport);
if (post_all_free) {
ocs_sm_post_event(&sport->sm, OCS_EVT_ALL_CHILD_NODES_FREE, NULL);
}
node->sport = NULL;
node->sm.current_state = NULL;
ocs_node_lock_free(node);
ocs_lock_free(&node->pend_frames_lock);
ocs_lock_free(&node->active_ios_lock);
ocs_device_lock(ocs);
ocs_list_add_tail(&xport->nodes_free_list, node);
ocs_device_unlock(ocs);
if(ns != NULL) {
ocs_node_post_event(ns, OCS_EVT_RSCN_RCVD, NULL);
}
return rc;
}
void
ocs_node_force_free(ocs_node_t *node)
{
ocs_io_t *io;
ocs_io_t *next;
ocs_io_t *els;
ocs_io_t *els_next;
ocs_sm_disable(&node->sm);
ocs_strncpy(node->prev_state_name, node->current_state_name, sizeof(node->prev_state_name));
ocs_strncpy(node->current_state_name, "disabled", sizeof(node->current_state_name));
ocs_scsi_notify_node_force_free(node);
ocs_lock(&node->active_ios_lock);
ocs_list_foreach_safe(&node->active_ios, io, next) {
ocs_list_remove(&io->node->active_ios, io);
ocs_io_free(node->ocs, io);
}
ocs_unlock(&node->active_ios_lock);
ocs_lock(&node->active_ios_lock);
ocs_list_foreach_safe(&node->els_io_pend_list, els, els_next) {
ocs_list_remove(&node->els_io_pend_list, els);
ocs_io_free(node->ocs, els);
}
ocs_unlock(&node->active_ios_lock);
ocs_lock(&node->active_ios_lock);
ocs_list_foreach_safe(&node->els_io_active_list, els, els_next) {
ocs_list_remove(&node->els_io_active_list, els);
ocs_io_free(node->ocs, els);
}
ocs_unlock(&node->active_ios_lock);
ocs_node_purge_pending(node);
ocs_node_free(node);
}
int32_t
ocs_node_attach(ocs_node_t *node)
{
int32_t rc = 0;
ocs_sport_t *sport = node->sport;
ocs_domain_t *domain = sport->domain;
ocs_t *ocs = node->ocs;
if (!domain->attached) {
ocs_log_test(ocs, "Warning: ocs_node_attach with unattached domain\n");
return -1;
}
ocs_node_build_eui_name(node->wwpn, sizeof(node->wwpn), ocs_node_get_wwpn(node));
ocs_node_build_eui_name(node->wwnn, sizeof(node->wwnn), ocs_node_get_wwnn(node));
if (ocs->enable_hlm) {
ocs_node_group_init(node);
}
ocs_dma_copy_in(&node->sparm_dma_buf, node->service_params+4, sizeof(node->service_params)-4);
ocs_node_lock(node);
rc = ocs_hw_node_attach(&ocs->hw, &node->rnode, &node->sparm_dma_buf);
if (OCS_HW_RTN_IS_ERROR(rc)) {
ocs_log_test(ocs, "ocs_hw_node_attach failed: %d\n", rc);
}
ocs_node_unlock(node);
return rc;
}
void
ocs_node_fcid_display(uint32_t fc_id, char *buffer, uint32_t buffer_length)
{
switch (fc_id) {
case FC_ADDR_FABRIC:
ocs_snprintf(buffer, buffer_length, "fabric");
break;
case FC_ADDR_CONTROLLER:
ocs_snprintf(buffer, buffer_length, "fabctl");
break;
case FC_ADDR_NAMESERVER:
ocs_snprintf(buffer, buffer_length, "nserve");
break;
default:
if (FC_ADDR_IS_DOMAIN_CTRL(fc_id)) {
ocs_snprintf(buffer, buffer_length, "dctl%02x",
FC_ADDR_GET_DOMAIN_CTRL(fc_id));
} else {
ocs_snprintf(buffer, buffer_length, "%06x", fc_id);
}
break;
}
}
void
ocs_node_update_display_name(ocs_node_t *node)
{
uint32_t port_id = node->rnode.fc_id;
ocs_sport_t *sport = node->sport;
char portid_display[16];
ocs_assert(sport);
ocs_node_fcid_display(port_id, portid_display, sizeof(portid_display));
ocs_snprintf(node->display_name, sizeof(node->display_name), "%s.%s", sport->display_name, portid_display);
}
void
ocs_node_send_ls_io_cleanup(ocs_node_t *node)
{
ocs_t *ocs = node->ocs;
if (node->send_ls_acc != OCS_NODE_SEND_LS_ACC_NONE) {
ocs_assert(node->ls_acc_io);
ocs_log_debug(ocs, "[%s] cleaning up LS_ACC oxid=0x%x\n",
node->display_name, node->ls_acc_oxid);
node->ls_acc_io->hio = NULL;
ocs_els_io_free(node->ls_acc_io);
node->send_ls_acc = OCS_NODE_SEND_LS_ACC_NONE;
node->ls_acc_io = NULL;
}
}
void *
__ocs_node_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
int32_t rc;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER: {
ocs_node_hold_frames(node);
ocs_assert(ocs_node_active_ios_empty(node), NULL);
ocs_assert(ocs_els_io_list_empty(node, &node->els_io_active_list), NULL);
node->req_free = 1;
switch (node->shutdown_reason) {
case OCS_NODE_SHUTDOWN_IMPLICIT_LOGO:
ocs_assert(node->send_ls_acc == OCS_NODE_SEND_LS_ACC_PLOGI, NULL);
node_printf(node, "Shutdown reason: implicit logout, re-authenticate\n");
ocs_scsi_io_alloc_enable(node);
node->req_free = 0;
rc = ocs_node_attach(node);
ocs_node_transition(node, __ocs_d_wait_node_attach, NULL);
if (rc == OCS_HW_RTN_SUCCESS_SYNC) {
ocs_node_post_event(node, OCS_EVT_NODE_ATTACH_OK, NULL);
}
break;
case OCS_NODE_SHUTDOWN_EXPLICIT_LOGO: {
int8_t pend_frames_empty;
ocs_node_send_ls_io_cleanup(node);
ocs_assert(ocs_els_io_list_empty(node, &node->els_io_pend_list), NULL);
ocs_lock(&node->pend_frames_lock);
pend_frames_empty = ocs_list_empty(&node->pend_frames);
ocs_unlock(&node->pend_frames_lock);
node_printf(node, "Shutdown: explicit logo pend=%d sport.ini=%d node.tgt=%d\n",
!pend_frames_empty, node->sport->enable_ini, node->targ);
if((!pend_frames_empty) || (node->sport->enable_ini && node->targ)) {
uint8_t send_plogi = FALSE;
if (node->sport->enable_ini && node->targ) {
send_plogi = TRUE;
}
ocs_scsi_io_alloc_enable(node);
node->req_free = 0;
ocs_node_init_device(node, send_plogi);
}
break;
}
case OCS_NODE_SHUTDOWN_DEFAULT:
default:
ocs_node_send_ls_io_cleanup(node);
ocs_assert(ocs_els_io_list_empty(node, &node->els_io_pend_list), NULL);
node_printf(node, "Shutdown reason: default, purge pending\n");
ocs_node_purge_pending(node);
break;
}
break;
}
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
default:
__ocs_node_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
static int
ocs_node_check_els_quiesced(ocs_node_t *node)
{
ocs_assert(node, -1);
if ((node->els_req_cnt == 0) && (node->els_cmpl_cnt == 0) &&
ocs_els_io_list_empty(node, &node->els_io_active_list)) {
if (!node->attached) {
node_printf(node, "HW node not attached\n");
ocs_node_transition(node, __ocs_node_wait_ios_shutdown, NULL);
} else {
node_printf(node, "HW node still attached\n");
ocs_node_transition(node, __ocs_node_wait_node_free, NULL);
}
return 1;
}
return 0;
}
void
ocs_node_initiate_cleanup(ocs_node_t *node)
{
ocs_io_t *els;
ocs_io_t *els_next;
ocs_t *ocs;
ocs_assert(node);
ocs = node->ocs;
ocs_lock(&node->active_ios_lock);
ocs_list_foreach_safe(&node->els_io_pend_list, els, els_next) {
if ((node->send_ls_acc != OCS_NODE_SEND_LS_ACC_NONE) &&
(els == node->ls_acc_io)) {
continue;
}
node_printf(node, "Freeing pending els %s\n", els->display_name);
ocs_list_remove(&node->els_io_pend_list, els);
ocs_io_free(node->ocs, els);
}
ocs_unlock(&node->active_ios_lock);
if (node->ls_acc_io && node->ls_acc_io->hio != NULL) {
ocs_assert(node->shutdown_reason == OCS_NODE_SHUTDOWN_IMPLICIT_LOGO);
ocs_assert(node->send_ls_acc == OCS_NODE_SEND_LS_ACC_PLOGI);
node_printf(node, "invalidating ls_acc_io due to implicit logo\n");
ocs_hw_io_free(&ocs->hw, node->ls_acc_io->hio);
node->ls_acc_io->hio = NULL;
}
if (ocs_node_check_els_quiesced(node) == 0) {
ocs_node_abort_all_els(node);
ocs_node_transition(node, __ocs_node_wait_els_shutdown, NULL);
}
}
void *
__ocs_node_wait_els_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
uint8_t check_quiesce = FALSE;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER: {
ocs_node_hold_frames(node);
if (ocs_els_io_list_empty(node, &node->els_io_active_list)) {
node_printf(node, "All ELS IOs complete\n");
check_quiesce = TRUE;
}
break;
}
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_REQ_OK:
case OCS_EVT_SRRS_ELS_REQ_FAIL:
case OCS_EVT_SRRS_ELS_REQ_RJT:
case OCS_EVT_ELS_REQ_ABORTED:
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
check_quiesce = TRUE;
break;
case OCS_EVT_SRRS_ELS_CMPL_OK:
case OCS_EVT_SRRS_ELS_CMPL_FAIL:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
check_quiesce = TRUE;
break;
case OCS_EVT_ALL_CHILD_NODES_FREE:
node_printf(node, "All ELS IOs complete\n");
ocs_assert(ocs_els_io_list_empty(node, &node->els_io_active_list), NULL);
check_quiesce = TRUE;
break;
case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
break;
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
case OCS_EVT_SHUTDOWN:
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO:
case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO:
node_printf(node, "%s received\n", ocs_sm_event_name(evt));
break;
default:
__ocs_node_common(__func__, ctx, evt, arg);
return NULL;
}
if (check_quiesce) {
ocs_node_check_els_quiesced(node);
}
return NULL;
}
void *
__ocs_node_wait_node_free(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
break;
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_NODE_FREE_OK:
node->attached = FALSE;
ocs_node_transition(node, __ocs_node_wait_ios_shutdown, NULL);
break;
case OCS_EVT_ALL_CHILD_NODES_FREE:
case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
break;
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
case OCS_EVT_SHUTDOWN:
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO:
case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO:
node_printf(node, "%s received\n", ocs_sm_event_name(evt));
break;
default:
__ocs_node_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_node_wait_ios_shutdown(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_io_t *io;
ocs_io_t *next;
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
ocs_node_hold_frames(node);
if (ocs_els_io_list_empty(node, &node->els_io_active_list)) {
if (!ocs_node_active_ios_empty(node)) {
ocs_lock(&node->active_ios_lock);
ocs_list_foreach_safe(&node->active_ios, io, next) {
ocs_list_remove(&io->node->active_ios, io);
ocs_io_free(node->ocs, io);
}
ocs_unlock(&node->active_ios_lock);
}
ocs_node_transition(node, __ocs_node_shutdown, NULL);
}
break;
case OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY:
case OCS_EVT_ALL_CHILD_NODES_FREE: {
if (ocs_node_active_ios_empty(node) &&
ocs_els_io_list_empty(node, &node->els_io_active_list)) {
ocs_node_transition(node, __ocs_node_shutdown, NULL);
}
break;
}
case OCS_EVT_EXIT:
ocs_node_accept_frames(node);
break;
case OCS_EVT_SRRS_ELS_REQ_FAIL:
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
break;
case OCS_EVT_SHUTDOWN:
node->shutdown_reason = OCS_NODE_SHUTDOWN_DEFAULT;
case OCS_EVT_SHUTDOWN_EXPLICIT_LOGO:
case OCS_EVT_SHUTDOWN_IMPLICIT_LOGO:
ocs_log_debug(ocs, "[%s] %-20s\n", node->display_name, ocs_sm_event_name(evt));
break;
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
default:
__ocs_node_common(__func__, ctx, evt, arg);
return NULL;
}
return NULL;
}
void *
__ocs_node_common(const char *funcname, ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
ocs_node_t *node = NULL;
ocs_t *ocs = NULL;
ocs_node_cb_t *cbdata = arg;
ocs_assert(ctx, NULL);
ocs_assert(ctx->app, NULL);
node = ctx->app;
ocs_assert(node->ocs, NULL);
ocs = node->ocs;
switch(evt) {
case OCS_EVT_ENTER:
case OCS_EVT_REENTER:
case OCS_EVT_EXIT:
case OCS_EVT_SPORT_TOPOLOGY_NOTIFY:
case OCS_EVT_NODE_MISSING:
case OCS_EVT_FCP_CMD_RCVD:
break;
case OCS_EVT_NODE_REFOUND:
node->refound = 1;
break;
case OCS_EVT_NODE_ATTACH_OK:
node->attached = TRUE;
break;
case OCS_EVT_NODE_FREE_OK:
case OCS_EVT_NODE_ATTACH_FAIL:
node->attached = FALSE;
break;
case OCS_EVT_SRRS_ELS_CMPL_OK:
case OCS_EVT_SRRS_ELS_CMPL_FAIL:
ocs_assert(node->els_cmpl_cnt, NULL);
node->els_cmpl_cnt--;
break;
case OCS_EVT_SRRS_ELS_REQ_OK:
case OCS_EVT_SRRS_ELS_REQ_FAIL:
case OCS_EVT_SRRS_ELS_REQ_RJT:
case OCS_EVT_ELS_REQ_ABORTED:
ocs_assert(node->els_req_cnt, NULL);
node->els_req_cnt--;
break;
case OCS_EVT_ELS_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_log_debug(ocs, "[%s] (%s) ELS x%02x, LS_RJT not supported\n",
node->display_name, funcname, ((uint8_t*)cbdata->payload->dma.virt)[0]);
ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id),
FC_REASON_COMMAND_NOT_SUPPORTED, FC_EXPL_NO_ADDITIONAL, 0,
NULL, NULL);
break;
}
case OCS_EVT_PLOGI_RCVD:
case OCS_EVT_FLOGI_RCVD:
case OCS_EVT_LOGO_RCVD:
case OCS_EVT_PRLI_RCVD:
case OCS_EVT_PRLO_RCVD:
case OCS_EVT_PDISC_RCVD:
case OCS_EVT_FDISC_RCVD:
case OCS_EVT_ADISC_RCVD:
case OCS_EVT_RSCN_RCVD:
case OCS_EVT_SCR_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_log_debug(ocs, "[%s] (%s) %s sending ELS_RJT\n",
node->display_name, funcname, ocs_sm_event_name(evt));
ocs_send_ls_rjt(cbdata->io, ocs_be16toh(hdr->ox_id),
FC_REASON_UNABLE_TO_PERFORM, FC_EXPL_NO_ADDITIONAL, 0,
NULL, NULL);
break;
}
case OCS_EVT_GID_PT_RCVD:
case OCS_EVT_RFT_ID_RCVD:
case OCS_EVT_RFF_ID_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_log_debug(ocs, "[%s] (%s) %s sending CT_REJECT\n",
node->display_name, funcname, ocs_sm_event_name(evt));
ocs_send_ct_rsp(cbdata->io, hdr->ox_id, cbdata->payload->dma.virt, FCCT_HDR_CMDRSP_REJECT, FCCT_COMMAND_NOT_SUPPORTED, 0);
break;
}
case OCS_EVT_ABTS_RCVD: {
fc_header_t *hdr = cbdata->header->dma.virt;
ocs_log_debug(ocs, "[%s] (%s) %s sending BA_ACC\n",
node->display_name, funcname, ocs_sm_event_name(evt));
ocs_bls_send_acc_hdr(cbdata->io, hdr);
break;
}
default:
ocs_log_test(node->ocs, "[%s] %-20s %-20s not handled\n", node->display_name, funcname,
ocs_sm_event_name(evt));
break;
}
return NULL;
}
void
ocs_node_save_sparms(ocs_node_t *node, void *payload)
{
ocs_memcpy(node->service_params, payload, sizeof(node->service_params));
}
void
ocs_node_post_event(ocs_node_t *node, ocs_sm_event_t evt, void *arg)
{
int free_node = FALSE;
ocs_assert(node);
ocs_node_lock(node);
node->evtdepth ++;
ocs_sm_post_event(&node->sm, evt, arg);
if (!node->hold_frames && (node->evtdepth == 1)) {
ocs_process_node_pending(node);
}
node->evtdepth --;
if ((node->evtdepth == 0) && node->req_free) {
free_node = TRUE;
}
ocs_node_unlock(node);
if (free_node) {
ocs_node_free(node);
}
return;
}
void
ocs_node_transition(ocs_node_t *node, ocs_sm_function_t state, void *data)
{
ocs_sm_ctx_t *ctx = &node->sm;
ocs_node_lock(node);
if (ctx->current_state == state) {
ocs_node_post_event(node, OCS_EVT_REENTER, data);
} else {
ocs_node_post_event(node, OCS_EVT_EXIT, data);
ctx->current_state = state;
ocs_node_post_event(node, OCS_EVT_ENTER, data);
}
ocs_node_unlock(node);
}
void
ocs_node_build_eui_name(char *buffer, uint32_t buffer_len, uint64_t eui_name)
{
ocs_memset(buffer, 0, buffer_len);
ocs_snprintf(buffer, buffer_len, "eui.%016llx", (unsigned long long)eui_name);
}
uint64_t
ocs_node_get_wwpn(ocs_node_t *node)
{
fc_plogi_payload_t *sp = (fc_plogi_payload_t*) node->service_params;
return (((uint64_t)ocs_be32toh(sp->port_name_hi) << 32ll) | (ocs_be32toh(sp->port_name_lo)));
}
uint64_t
ocs_node_get_wwnn(ocs_node_t *node)
{
fc_plogi_payload_t *sp = (fc_plogi_payload_t*) node->service_params;
return (((uint64_t)ocs_be32toh(sp->node_name_hi) << 32ll) | (ocs_be32toh(sp->node_name_lo)));
}
int
ocs_ddump_node(ocs_textbuf_t *textbuf, ocs_node_t *node)
{
ocs_io_t *io;
ocs_io_t *els;
int retval = 0;
ocs_ddump_section(textbuf, "node", node->instance_index);
ocs_ddump_value(textbuf, "display_name", "%s", node->display_name);
ocs_ddump_value(textbuf, "current_state", "%s", node->current_state_name);
ocs_ddump_value(textbuf, "prev_state", "%s", node->prev_state_name);
ocs_ddump_value(textbuf, "current_evt", "%s", ocs_sm_event_name(node->current_evt));
ocs_ddump_value(textbuf, "prev_evt", "%s", ocs_sm_event_name(node->prev_evt));
ocs_ddump_value(textbuf, "indicator", "%#x", node->rnode.indicator);
ocs_ddump_value(textbuf, "fc_id", "%#06x", node->rnode.fc_id);
ocs_ddump_value(textbuf, "attached", "%d", node->rnode.attached);
ocs_ddump_value(textbuf, "hold_frames", "%d", node->hold_frames);
ocs_ddump_value(textbuf, "io_alloc_enabled", "%d", node->io_alloc_enabled);
ocs_ddump_value(textbuf, "shutdown_reason", "%d", node->shutdown_reason);
ocs_ddump_value(textbuf, "send_ls_acc", "%d", node->send_ls_acc);
ocs_ddump_value(textbuf, "ls_acc_did", "%d", node->ls_acc_did);
ocs_ddump_value(textbuf, "ls_acc_oxid", "%#04x", node->ls_acc_oxid);
ocs_ddump_value(textbuf, "req_free", "%d", node->req_free);
ocs_ddump_value(textbuf, "els_req_cnt", "%d", node->els_req_cnt);
ocs_ddump_value(textbuf, "els_cmpl_cnt", "%d", node->els_cmpl_cnt);
ocs_ddump_value(textbuf, "targ", "%d", node->targ);
ocs_ddump_value(textbuf, "init", "%d", node->init);
ocs_ddump_value(textbuf, "wwnn", "%s", node->wwnn);
ocs_ddump_value(textbuf, "wwpn", "%s", node->wwpn);
ocs_ddump_value(textbuf, "login_state", "%d", (node->sm.current_state == __ocs_d_device_ready) ? 1 : 0);
ocs_ddump_value(textbuf, "chained_io_count", "%d", node->chained_io_count);
ocs_ddump_value(textbuf, "abort_cnt", "%d", node->abort_cnt);
ocs_display_sparams(NULL, "node_sparams", 1, textbuf, node->service_params+4);
ocs_lock(&node->pend_frames_lock);
if (!ocs_list_empty(&node->pend_frames)) {
ocs_hw_sequence_t *frame;
ocs_ddump_section(textbuf, "pending_frames", 0);
ocs_list_foreach(&node->pend_frames, frame) {
fc_header_t *hdr;
char buf[128];
hdr = frame->header->dma.virt;
ocs_snprintf(buf, sizeof(buf), "%02x/%04x/%04x len %zu",
hdr->r_ctl, ocs_be16toh(hdr->ox_id), ocs_be16toh(hdr->rx_id),
frame->payload->dma.len);
ocs_ddump_value(textbuf, "frame", "%s", buf);
}
ocs_ddump_endsection(textbuf, "pending_frames", 0);
}
ocs_unlock(&node->pend_frames_lock);
ocs_scsi_ini_ddump(textbuf, OCS_SCSI_DDUMP_NODE, node);
ocs_scsi_tgt_ddump(textbuf, OCS_SCSI_DDUMP_NODE, node);
ocs_lock(&node->active_ios_lock);
ocs_ddump_section(textbuf, "active_ios", 0);
ocs_list_foreach(&node->active_ios, io) {
ocs_ddump_io(textbuf, io);
}
ocs_ddump_endsection(textbuf, "active_ios", 0);
ocs_ddump_section(textbuf, "els_io_pend_list", 0);
ocs_list_foreach(&node->els_io_pend_list, els) {
ocs_ddump_els(textbuf, els);
}
ocs_ddump_endsection(textbuf, "els_io_pend_list", 0);
ocs_ddump_section(textbuf, "els_io_active_list", 0);
ocs_list_foreach(&node->els_io_active_list, els) {
ocs_ddump_els(textbuf, els);
}
ocs_ddump_endsection(textbuf, "els_io_active_list", 0);
ocs_unlock(&node->active_ios_lock);
ocs_ddump_endsection(textbuf, "node", node->instance_index);
return retval;
}
int32_t
node_check_els_req(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg, uint8_t cmd, ocs_node_common_func_t node_common_func, const char *funcname)
{
ocs_node_t *node = NULL;
ocs_t *ocs = NULL;
ocs_node_cb_t *cbdata = arg;
fc_els_gen_t *els_gen = NULL;
ocs_assert(ctx, -1);
node = ctx->app;
ocs_assert(node, -1);
ocs = node->ocs;
ocs_assert(ocs, -1);
cbdata = arg;
ocs_assert(cbdata, -1);
ocs_assert(cbdata->els, -1);
els_gen = (fc_els_gen_t *)cbdata->els->els_req.virt;
ocs_assert(els_gen, -1);
if ((cbdata->els->hio_type != OCS_HW_ELS_REQ) || (els_gen->command_code != cmd)) {
if (cbdata->els->hio_type != OCS_HW_ELS_REQ) {
ocs_log_debug(node->ocs, "[%s] %-20s expecting ELS cmd=x%x received type=%d\n",
node->display_name, funcname, cmd, cbdata->els->hio_type);
} else {
ocs_log_debug(node->ocs, "[%s] %-20s expecting ELS cmd=x%x received cmd=x%x\n",
node->display_name, funcname, cmd, els_gen->command_code);
}
node_common_func(funcname, ctx, evt, arg);
return -1;
}
return 0;
}
int32_t
node_check_ns_req(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg, uint32_t cmd, ocs_node_common_func_t node_common_func, const char *funcname)
{
ocs_node_t *node = NULL;
ocs_t *ocs = NULL;
ocs_node_cb_t *cbdata = arg;
fcct_iu_header_t *fcct = NULL;
ocs_assert(ctx, -1);
node = ctx->app;
ocs_assert(node, -1);
ocs = node->ocs;
ocs_assert(ocs, -1);
cbdata = arg;
ocs_assert(cbdata, -1);
ocs_assert(cbdata->els, -1);
fcct = (fcct_iu_header_t *)cbdata->els->els_req.virt;
ocs_assert(fcct, -1);
if ((cbdata->els->hio_type != OCS_HW_FC_CT) || fcct->cmd_rsp_code != ocs_htobe16(cmd)) {
if (cbdata->els->hio_type != OCS_HW_FC_CT) {
ocs_log_debug(node->ocs, "[%s] %-20s expecting NS cmd=x%x received type=%d\n",
node->display_name, funcname, cmd, cbdata->els->hio_type);
} else {
ocs_log_debug(node->ocs, "[%s] %-20s expecting NS cmd=x%x received cmd=x%x\n",
node->display_name, funcname, cmd, fcct->cmd_rsp_code);
}
node_common_func(funcname, ctx, evt, arg);
return -1;
}
return 0;
}
void
ocs_mgmt_node_list(ocs_textbuf_t *textbuf, void *object)
{
ocs_io_t *io;
ocs_node_t *node = (ocs_node_t *)object;
ocs_mgmt_start_section(textbuf, "node", node->instance_index);
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "display_name");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "indicator");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "fc_id");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "attached");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "hold_frames");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "shutting_down");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "req_free");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "ox_id");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "ox_id_in_use");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "abort_cnt");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "targ");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "init");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "wwpn");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "wwnn");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "pend_frames");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "chained_io_count");
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_EX, "resume");
ocs_lock(&node->active_ios_lock);
ocs_list_foreach(&node->active_ios, io) {
if ((io->mgmt_functions) && (io->mgmt_functions->get_list_handler)) {
io->mgmt_functions->get_list_handler(textbuf, io);
}
}
ocs_unlock(&node->active_ios_lock);
ocs_mgmt_end_section(textbuf, "node", node->instance_index);
}
int
ocs_mgmt_node_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *object)
{
ocs_io_t *io;
ocs_node_t *node = (ocs_node_t *)object;
char qualifier[80];
int retval = -1;
ocs_mgmt_start_section(textbuf, "node", node->instance_index);
ocs_snprintf(qualifier, sizeof(qualifier), "%s/node[%d]", parent, node->instance_index);
if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
char *unqualified_name = name + strlen(qualifier) +1;
if (ocs_strcmp(unqualified_name, "display_name") == 0) {
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", node->display_name);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "indicator") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "0x%x", node->rnode.indicator);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "fc_id") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fc_id", "0x%06x", node->rnode.fc_id);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "attached") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "attached", node->rnode.attached);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "hold_frames") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "hold_frames", node->hold_frames);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "io_alloc_enabled") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "io_alloc_enabled", node->io_alloc_enabled);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "req_free") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "req_free", node->req_free);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "ls_acc_oxid") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "ls_acc_oxid", "0x%#04x", node->ls_acc_oxid);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "ls_acc_did") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "ls_acc_did", "0x%#04x", node->ls_acc_did);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "abort_cnt") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "abort_cnt", "%d", node->abort_cnt);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "targ") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "targ", node->targ);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "init") == 0) {
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "init", node->init);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "wwpn") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwpn", "%s", node->wwpn);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "wwnn") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwnn", "%s", node->wwnn);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "current_state") == 0) {
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "current_state", node->current_state_name);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "login_state") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "login_state", "%d", (node->sm.current_state == __ocs_d_device_ready) ? 1 : 0);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "pend_frames") == 0) {
ocs_hw_sequence_t *frame;
ocs_lock(&node->pend_frames_lock);
ocs_list_foreach(&node->pend_frames, frame) {
fc_header_t *hdr;
char buf[128];
hdr = frame->header->dma.virt;
ocs_snprintf(buf, sizeof(buf), "%02x/%04x/%04x len %zu", hdr->r_ctl,
ocs_be16toh(hdr->ox_id), ocs_be16toh(hdr->rx_id),
frame->payload->dma.len);
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "pend_frames", buf);
}
ocs_unlock(&node->pend_frames_lock);
retval = 0;
} else if (ocs_strcmp(unqualified_name, "chained_io_count") == 0) {
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "chained_io_count", "%d", node->chained_io_count);
retval = 0;
} else {
ocs_lock(&node->active_ios_lock);
ocs_list_foreach(&node->active_ios, io) {
if ((io->mgmt_functions) && (io->mgmt_functions->get_handler)) {
retval = io->mgmt_functions->get_handler(textbuf, qualifier, name, io);
}
if (retval == 0) {
break;
}
}
ocs_unlock(&node->active_ios_lock);
}
}
ocs_mgmt_end_section(textbuf, "node", node->instance_index);
return retval;
}
void
ocs_mgmt_node_get_all(ocs_textbuf_t *textbuf, void *object)
{
ocs_io_t *io;
ocs_node_t *node = (ocs_node_t *)object;
ocs_hw_sequence_t *frame;
ocs_mgmt_start_section(textbuf, "node", node->instance_index);
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", node->display_name);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "indicator", "0x%x", node->rnode.indicator);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fc_id", "0x%06x", node->rnode.fc_id);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "attached", node->rnode.attached);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "hold_frames", node->hold_frames);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "io_alloc_enabled", node->io_alloc_enabled);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "req_free", node->req_free);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "ls_acc_oxid", "0x%#04x", node->ls_acc_oxid);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "ls_acc_did", "0x%#04x", node->ls_acc_did);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "abort_cnt", "%d", node->abort_cnt);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "targ", node->targ);
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "init", node->init);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwpn", "%s", node->wwpn);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwnn", "%s", node->wwnn);
ocs_lock(&node->pend_frames_lock);
ocs_list_foreach(&node->pend_frames, frame) {
fc_header_t *hdr;
char buf[128];
hdr = frame->header->dma.virt;
ocs_snprintf(buf, sizeof(buf), "%02x/%04x/%04x len %zu", hdr->r_ctl,
ocs_be16toh(hdr->ox_id), ocs_be16toh(hdr->rx_id),
frame->payload->dma.len);
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "pend_frames", buf);
}
ocs_unlock(&node->pend_frames_lock);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "chained_io_count", "%d", node->chained_io_count);
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_EX, "resume");
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "current_state", node->current_state_name);
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "login_state", "%d", (node->sm.current_state == __ocs_d_device_ready) ? 1 : 0);
ocs_lock(&node->active_ios_lock);
ocs_list_foreach(&node->active_ios, io) {
if ((io->mgmt_functions) && (io->mgmt_functions->get_all_handler)) {
io->mgmt_functions->get_all_handler(textbuf,io);
}
}
ocs_unlock(&node->active_ios_lock);
ocs_mgmt_end_section(textbuf, "node", node->instance_index);
}
int
ocs_mgmt_node_set(char *parent, char *name, char *value, void *object)
{
ocs_io_t *io;
ocs_node_t *node = (ocs_node_t *)object;
char qualifier[80];
int retval = -1;
ocs_snprintf(qualifier, sizeof(qualifier), "%s/node[%d]", parent, node->instance_index);
if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
ocs_lock(&node->active_ios_lock);
ocs_list_foreach(&node->active_ios, io) {
if ((io->mgmt_functions) && (io->mgmt_functions->set_handler)) {
retval = io->mgmt_functions->set_handler(qualifier, name, value, io);
}
if (retval == 0) {
break;
}
}
ocs_unlock(&node->active_ios_lock);
}
return retval;
}
int
ocs_mgmt_node_exec(char *parent, char *action, void *arg_in, uint32_t arg_in_length,
void *arg_out, uint32_t arg_out_length, void *object)
{
ocs_io_t *io;
ocs_node_t *node = (ocs_node_t *)object;
char qualifier[80];
int retval = -1;
ocs_snprintf(qualifier, sizeof(qualifier), "%s.node%d", parent, node->instance_index);
if (ocs_strncmp(action, qualifier, strlen(qualifier)) == 0) {
char *unqualified_name = action + strlen(qualifier) +1;
if (ocs_strcmp(unqualified_name, "resume") == 0) {
ocs_node_post_event(node, OCS_EVT_RESUME, NULL);
}
{
ocs_lock(&node->active_ios_lock);
ocs_list_foreach(&node->active_ios, io) {
if ((io->mgmt_functions) && (io->mgmt_functions->exec_handler)) {
retval = io->mgmt_functions->exec_handler(qualifier, action, arg_in, arg_in_length,
arg_out, arg_out_length, io);
}
if (retval == 0) {
break;
}
}
ocs_unlock(&node->active_ios_lock);
}
}
return retval;
}
int
ocs_node_active_ios_empty(ocs_node_t *node)
{
int empty;
ocs_lock(&node->active_ios_lock);
empty = ocs_list_empty(&node->active_ios);
ocs_unlock(&node->active_ios_lock);
return empty;
}
void
ocs_node_pause(ocs_node_t *node, ocs_sm_function_t state)
{
node->nodedb_state = state;
ocs_node_transition(node, __ocs_node_paused, NULL);
}
void *
__ocs_node_paused(ocs_sm_ctx_t *ctx, ocs_sm_event_t evt, void *arg)
{
std_node_state_decl();
node_sm_trace();
switch(evt) {
case OCS_EVT_ENTER:
node_printf(node, "Paused\n");
break;
case OCS_EVT_RESUME: {
ocs_sm_function_t pf = node->nodedb_state;
node->nodedb_state = NULL;
ocs_node_transition(node, pf, NULL);
break;
}
case OCS_EVT_DOMAIN_ATTACH_OK:
break;
case OCS_EVT_SHUTDOWN:
node->req_free = 1;
break;
default:
__ocs_node_common(__func__, ctx, evt, arg);
break;
}
return NULL;
}
int32_t
ocs_node_resume(ocs_node_t *node)
{
ocs_assert(node != NULL, -1);
ocs_node_post_event(node, OCS_EVT_RESUME, NULL);
return 0;
}
int32_t
ocs_node_recv_els_frame(ocs_node_t *node, ocs_hw_sequence_t *seq)
{
struct {
uint32_t cmd;
ocs_sm_event_t evt;
uint32_t payload_size;
} els_cmd_list[] = {
{FC_ELS_CMD_PLOGI, OCS_EVT_PLOGI_RCVD, sizeof(fc_plogi_payload_t)},
{FC_ELS_CMD_FLOGI, OCS_EVT_FLOGI_RCVD, sizeof(fc_plogi_payload_t)},
{FC_ELS_CMD_LOGO, OCS_EVT_LOGO_RCVD, sizeof(fc_acc_payload_t)},
{FC_ELS_CMD_RRQ, OCS_EVT_RRQ_RCVD, sizeof(fc_acc_payload_t)},
{FC_ELS_CMD_PRLI, OCS_EVT_PRLI_RCVD, sizeof(fc_prli_payload_t)},
{FC_ELS_CMD_PRLO, OCS_EVT_PRLO_RCVD, sizeof(fc_prlo_payload_t)},
{FC_ELS_CMD_PDISC, OCS_EVT_PDISC_RCVD, MAX_ACC_REJECT_PAYLOAD},
{FC_ELS_CMD_FDISC, OCS_EVT_FDISC_RCVD, MAX_ACC_REJECT_PAYLOAD},
{FC_ELS_CMD_ADISC, OCS_EVT_ADISC_RCVD, sizeof(fc_adisc_payload_t)},
{FC_ELS_CMD_RSCN, OCS_EVT_RSCN_RCVD, MAX_ACC_REJECT_PAYLOAD},
{FC_ELS_CMD_SCR , OCS_EVT_SCR_RCVD, MAX_ACC_REJECT_PAYLOAD},
};
ocs_t *ocs = node->ocs;
ocs_node_cb_t cbdata;
fc_header_t *hdr = seq->header->dma.virt;
uint8_t *buf = seq->payload->dma.virt;
ocs_sm_event_t evt = OCS_EVT_ELS_RCVD;
uint32_t payload_size = MAX_ACC_REJECT_PAYLOAD;
uint32_t i;
ocs_memset(&cbdata, 0, sizeof(cbdata));
cbdata.header = seq->header;
cbdata.payload = seq->payload;
for (i = 0; i < ARRAY_SIZE(els_cmd_list); i ++) {
if (els_cmd_list[i].cmd == buf[0]) {
evt = els_cmd_list[i].evt;
payload_size = els_cmd_list[i].payload_size;
break;
}
}
switch(evt) {
case OCS_EVT_FLOGI_RCVD:
ocs_display_sparams(node->display_name, "flogi rcvd req", 0, NULL, ((uint8_t*)seq->payload->dma.virt)+4);
break;
case OCS_EVT_FDISC_RCVD:
ocs_display_sparams(node->display_name, "fdisc rcvd req", 0, NULL, ((uint8_t*)seq->payload->dma.virt)+4);
break;
case OCS_EVT_PLOGI_RCVD:
ocs_display_sparams(node->display_name, "plogi rcvd req", 0, NULL, ((uint8_t*)seq->payload->dma.virt)+4);
break;
default:
break;
}
cbdata.io = ocs_els_io_alloc(node, payload_size, OCS_ELS_ROLE_RESPONDER);
if (cbdata.io != NULL) {
cbdata.io->hw_priv = seq->hw_priv;
cbdata.io->seq_init = 1;
ocs_node_post_event(node, evt, &cbdata);
} else {
node_printf(node, "failure to allocate SCSI IO for ELS s_id %06x d_id %06x ox_id %04x rx_id %04x\n",
fc_be24toh(hdr->s_id), fc_be24toh(hdr->d_id), ocs_be16toh(hdr->ox_id), ocs_be16toh(hdr->rx_id));
}
ocs_hw_sequence_free(&ocs->hw, seq);
return 0;
}
int32_t
ocs_node_recv_abts_frame(ocs_node_t *node, ocs_hw_sequence_t *seq)
{
ocs_t *ocs = node->ocs;
ocs_xport_t *xport = ocs->xport;
fc_header_t *hdr = seq->header->dma.virt;
uint16_t ox_id = ocs_be16toh(hdr->ox_id);
uint16_t rx_id = ocs_be16toh(hdr->rx_id);
ocs_node_cb_t cbdata;
int32_t rc = 0;
node->abort_cnt++;
if (ocs_io_find_tgt_io(ocs, node, ox_id, rx_id) == NULL) {
uint32_t send_frame_capable;
ocs_log_debug(ocs, "IO not found (ox_id %04x)\n", ox_id);
rc = ocs_hw_get(&ocs->hw, OCS_HW_SEND_FRAME_CAPABLE, &send_frame_capable);
if ((rc == 0) && send_frame_capable) {
rc = ocs_sframe_send_bls_acc(node, seq);
if (rc) {
ocs_log_test(ocs, "ocs_bls_acc_send_frame failed\n");
}
return rc;
}
}
ocs_memset(&cbdata, 0, sizeof(cbdata));
cbdata.header = seq->header;
cbdata.payload = seq->payload;
cbdata.io = ocs_scsi_io_alloc(node, OCS_SCSI_IO_ROLE_RESPONDER);
if (cbdata.io != NULL) {
cbdata.io->hw_priv = seq->hw_priv;
cbdata.io->seq_init = 1;
cbdata.io->ocs = ocs;
cbdata.io->node = node;
cbdata.io->cmd_tgt = TRUE;
ocs_node_post_event(node, OCS_EVT_ABTS_RCVD, &cbdata);
} else {
ocs_atomic_add_return(&xport->io_alloc_failed_count, 1);
node_printf(node, "SCSI IO allocation failed for ABTS received s_id %06x d_id %06x ox_id %04x rx_id %04x\n",
fc_be24toh(hdr->s_id), fc_be24toh(hdr->d_id), ocs_be16toh(hdr->ox_id), ocs_be16toh(hdr->rx_id));
}
ocs_hw_sequence_free(&ocs->hw, seq);
return 0;
}
int32_t
ocs_node_recv_ct_frame(ocs_node_t *node, ocs_hw_sequence_t *seq)
{
ocs_t *ocs = node->ocs;
fc_header_t *hdr = seq->header->dma.virt;
fcct_iu_header_t *iu = seq->payload->dma.virt;
ocs_sm_event_t evt = OCS_EVT_ELS_RCVD;
uint32_t payload_size = MAX_ACC_REJECT_PAYLOAD;
uint16_t gscmd = ocs_be16toh(iu->cmd_rsp_code);
ocs_node_cb_t cbdata;
uint32_t i;
struct {
uint32_t cmd;
ocs_sm_event_t evt;
uint32_t payload_size;
} ct_cmd_list[] = {
{FC_GS_NAMESERVER_RFF_ID, OCS_EVT_RFF_ID_RCVD, 100},
{FC_GS_NAMESERVER_RFT_ID, OCS_EVT_RFT_ID_RCVD, 100},
{FC_GS_NAMESERVER_GNN_ID, OCS_EVT_GNN_ID_RCVD, 100},
{FC_GS_NAMESERVER_GPN_ID, OCS_EVT_GPN_ID_RCVD, 100},
{FC_GS_NAMESERVER_GFPN_ID, OCS_EVT_GFPN_ID_RCVD, 100},
{FC_GS_NAMESERVER_GFF_ID, OCS_EVT_GFF_ID_RCVD, 100},
{FC_GS_NAMESERVER_GID_FT, OCS_EVT_GID_FT_RCVD, 256},
{FC_GS_NAMESERVER_GID_PT, OCS_EVT_GID_PT_RCVD, 256},
{FC_GS_NAMESERVER_RPN_ID, OCS_EVT_RPN_ID_RCVD, 100},
{FC_GS_NAMESERVER_RNN_ID, OCS_EVT_RNN_ID_RCVD, 100},
{FC_GS_NAMESERVER_RCS_ID, OCS_EVT_RCS_ID_RCVD, 100},
{FC_GS_NAMESERVER_RSNN_NN, OCS_EVT_RSNN_NN_RCVD, 100},
{FC_GS_NAMESERVER_RSPN_ID, OCS_EVT_RSPN_ID_RCVD, 100},
{FC_GS_NAMESERVER_RHBA, OCS_EVT_RHBA_RCVD, 100},
{FC_GS_NAMESERVER_RPA, OCS_EVT_RPA_RCVD, 100},
};
ocs_memset(&cbdata, 0, sizeof(cbdata));
cbdata.header = seq->header;
cbdata.payload = seq->payload;
for (i = 0; i < ARRAY_SIZE(ct_cmd_list); i ++) {
if (ct_cmd_list[i].cmd == gscmd) {
evt = ct_cmd_list[i].evt;
payload_size = ct_cmd_list[i].payload_size;
break;
}
}
cbdata.io = ocs_els_io_alloc(node, payload_size, OCS_ELS_ROLE_RESPONDER);
if (cbdata.io == NULL) {
node_printf(node, "GS IO failed for s_id %06x d_id %06x ox_id %04x rx_id %04x\n",
fc_be24toh(hdr->s_id), fc_be24toh(hdr->d_id),
ocs_be16toh(hdr->ox_id), ocs_be16toh(hdr->rx_id));
return -1;
}
cbdata.io->hw_priv = seq->hw_priv;
ocs_node_post_event(node, evt, &cbdata);
ocs_hw_sequence_free(&ocs->hw, seq);
return 0;
}
int32_t
ocs_node_recv_fcp_cmd(ocs_node_t *node, ocs_hw_sequence_t *seq)
{
ocs_node_cb_t cbdata;
ocs_t *ocs = node->ocs;
ocs_memset(&cbdata, 0, sizeof(cbdata));
cbdata.header = seq->header;
cbdata.payload = seq->payload;
ocs_node_post_event(node, OCS_EVT_FCP_CMD_RCVD, &cbdata);
ocs_hw_sequence_free(&ocs->hw, seq);
return 0;
}
int32_t
ocs_node_recv_bls_no_sit(ocs_node_t *node, ocs_hw_sequence_t *seq)
{
fc_header_t *hdr = seq->header->dma.virt;
node_printf(node, "Dropping frame hdr = %08x %08x %08x %08x %08x %08x\n",
ocs_htobe32(((uint32_t *)hdr)[0]),
ocs_htobe32(((uint32_t *)hdr)[1]),
ocs_htobe32(((uint32_t *)hdr)[2]),
ocs_htobe32(((uint32_t *)hdr)[3]),
ocs_htobe32(((uint32_t *)hdr)[4]),
ocs_htobe32(((uint32_t *)hdr)[5]));
return -1;
}