linux/drivers/scsi/bfa/fabric.c
Krishna Gudipati e67143243a [SCSI] bfa: Resume BFA operations after firmware mismatch is resolved.
bfad.c & bfad_drv.h:
  * Created a kernel thread from pci_probe that does the bfad start
    operations after BFA init done on a firmware mismatch.
  * The kernel thread on a fw mismatch waits for an event from IOC
    call back and is woken up from bfa_cb_init() on BFA init success.
  * In normal cases of no firmware mismatch this thread is terminated
    in pci_probe.

bfa_fcs_lport.c, fabric.c, fcs_lport.h & vport.c:
  * Split the lport init to attach time and init time code, so that
    proper config attributes are set after firmware mismatch.

bfa_iocfc.c:
  * Handle an IOC timer issue, where the IOC timer would expire before
    the init completion and send Init fail event to the driver,
    however IOC init continues and completes successfully at the later
    stage. The bfa and driver were not handling this kind of deferred
    init completion.

Signed-off-by: Krishna Gudipati <kgudipat@brocade.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
2010-03-04 16:17:02 +05:30

1284 lines
32 KiB
C

/*
* Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
* All rights reserved
* www.brocade.com
*
* Linux driver for Brocade Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
* published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
/**
* fabric.c Fabric module implementation.
*/
#include "fcs_fabric.h"
#include "fcs_lport.h"
#include "fcs_vport.h"
#include "fcs_trcmod.h"
#include "fcs_fcxp.h"
#include "fcs_auth.h"
#include "fcs.h"
#include "fcbuild.h"
#include <log/bfa_log_fcs.h>
#include <aen/bfa_aen_port.h>
#include <bfa_svc.h>
BFA_TRC_FILE(FCS, FABRIC);
#define BFA_FCS_FABRIC_RETRY_DELAY (2000) /* Milliseconds */
#define BFA_FCS_FABRIC_CLEANUP_DELAY (10000) /* Milliseconds */
#define bfa_fcs_fabric_set_opertype(__fabric) do { \
if (bfa_pport_get_topology((__fabric)->fcs->bfa) \
== BFA_PPORT_TOPOLOGY_P2P) \
(__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \
else \
(__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \
} while (0)
/*
* forward declarations
*/
static void bfa_fcs_fabric_init(struct bfa_fcs_fabric_s *fabric);
static void bfa_fcs_fabric_login(struct bfa_fcs_fabric_s *fabric);
static void bfa_fcs_fabric_notify_online(struct bfa_fcs_fabric_s *fabric);
static void bfa_fcs_fabric_notify_offline(struct bfa_fcs_fabric_s *fabric);
static void bfa_fcs_fabric_delay(void *cbarg);
static void bfa_fcs_fabric_delete(struct bfa_fcs_fabric_s *fabric);
static void bfa_fcs_fabric_delete_comp(void *cbarg);
static void bfa_fcs_fabric_process_uf(struct bfa_fcs_fabric_s *fabric,
struct fchs_s *fchs, u16 len);
static void bfa_fcs_fabric_process_flogi(struct bfa_fcs_fabric_s *fabric,
struct fchs_s *fchs, u16 len);
static void bfa_fcs_fabric_send_flogi_acc(struct bfa_fcs_fabric_s *fabric);
static void bfa_fcs_fabric_flogiacc_comp(void *fcsarg,
struct bfa_fcxp_s *fcxp,
void *cbarg, bfa_status_t status,
u32 rsp_len,
u32 resid_len,
struct fchs_s *rspfchs);
/**
* fcs_fabric_sm fabric state machine functions
*/
/**
* Fabric state machine events
*/
enum bfa_fcs_fabric_event {
BFA_FCS_FABRIC_SM_CREATE = 1, /* fabric create from driver */
BFA_FCS_FABRIC_SM_DELETE = 2, /* fabric delete from driver */
BFA_FCS_FABRIC_SM_LINK_DOWN = 3, /* link down from port */
BFA_FCS_FABRIC_SM_LINK_UP = 4, /* link up from port */
BFA_FCS_FABRIC_SM_CONT_OP = 5, /* continue op from flogi/auth */
BFA_FCS_FABRIC_SM_RETRY_OP = 6, /* continue op from flogi/auth */
BFA_FCS_FABRIC_SM_NO_FABRIC = 7, /* no fabric from flogi/auth
*/
BFA_FCS_FABRIC_SM_PERF_EVFP = 8, /* perform EVFP from
*flogi/auth */
BFA_FCS_FABRIC_SM_ISOLATE = 9, /* isolate from EVFP processing */
BFA_FCS_FABRIC_SM_NO_TAGGING = 10,/* no VFT tagging from EVFP */
BFA_FCS_FABRIC_SM_DELAYED = 11, /* timeout delay event */
BFA_FCS_FABRIC_SM_AUTH_FAILED = 12, /* authentication failed */
BFA_FCS_FABRIC_SM_AUTH_SUCCESS = 13, /* authentication successful
*/
BFA_FCS_FABRIC_SM_DELCOMP = 14, /* all vports deleted event */
BFA_FCS_FABRIC_SM_LOOPBACK = 15, /* Received our own FLOGI */
BFA_FCS_FABRIC_SM_START = 16, /* fabric delete from driver */
};
static void bfa_fcs_fabric_sm_uninit(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_flogi(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_flogi_retry(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_auth(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_auth_failed(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_loopback(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_nofabric(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_evfp(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_evfp_done(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_isolated(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
static void bfa_fcs_fabric_sm_deleting(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event);
/**
* Beginning state before fabric creation.
*/
static void
bfa_fcs_fabric_sm_uninit(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_CREATE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created);
bfa_fcs_fabric_init(fabric);
bfa_fcs_lport_init(&fabric->bport, &fabric->bport.port_cfg);
break;
case BFA_FCS_FABRIC_SM_LINK_UP:
case BFA_FCS_FABRIC_SM_LINK_DOWN:
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* Beginning state before fabric creation.
*/
static void
bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_START:
if (bfa_pport_is_linkup(fabric->fcs->bfa)) {
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi);
bfa_fcs_fabric_login(fabric);
} else
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
break;
case BFA_FCS_FABRIC_SM_LINK_UP:
case BFA_FCS_FABRIC_SM_LINK_DOWN:
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit);
bfa_fcs_modexit_comp(fabric->fcs);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* Link is down, awaiting LINK UP event from port. This is also the
* first state at fabric creation.
*/
static void
bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_LINK_UP:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi);
bfa_fcs_fabric_login(fabric);
break;
case BFA_FCS_FABRIC_SM_RETRY_OP:
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
bfa_fcs_fabric_delete(fabric);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* FLOGI is in progress, awaiting FLOGI reply.
*/
static void
bfa_fcs_fabric_sm_flogi(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_CONT_OP:
bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit);
fabric->fab_type = BFA_FCS_FABRIC_SWITCHED;
if (fabric->auth_reqd && fabric->is_auth) {
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth);
bfa_trc(fabric->fcs, event);
} else {
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online);
bfa_fcs_fabric_notify_online(fabric);
}
break;
case BFA_FCS_FABRIC_SM_RETRY_OP:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi_retry);
bfa_timer_start(fabric->fcs->bfa, &fabric->delay_timer,
bfa_fcs_fabric_delay, fabric,
BFA_FCS_FABRIC_RETRY_DELAY);
break;
case BFA_FCS_FABRIC_SM_LOOPBACK:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_loopback);
bfa_lps_discard(fabric->lps);
bfa_fcs_fabric_set_opertype(fabric);
break;
case BFA_FCS_FABRIC_SM_NO_FABRIC:
fabric->fab_type = BFA_FCS_FABRIC_N2N;
bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit);
bfa_fcs_fabric_notify_online(fabric);
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_nofabric);
break;
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
bfa_lps_discard(fabric->lps);
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
bfa_lps_discard(fabric->lps);
bfa_fcs_fabric_delete(fabric);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
static void
bfa_fcs_fabric_sm_flogi_retry(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_DELAYED:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi);
bfa_fcs_fabric_login(fabric);
break;
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
bfa_timer_stop(&fabric->delay_timer);
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
bfa_timer_stop(&fabric->delay_timer);
bfa_fcs_fabric_delete(fabric);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* Authentication is in progress, awaiting authentication results.
*/
static void
bfa_fcs_fabric_sm_auth(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_AUTH_FAILED:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth_failed);
bfa_lps_discard(fabric->lps);
break;
case BFA_FCS_FABRIC_SM_AUTH_SUCCESS:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online);
bfa_fcs_fabric_notify_online(fabric);
break;
case BFA_FCS_FABRIC_SM_PERF_EVFP:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_evfp);
break;
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
bfa_lps_discard(fabric->lps);
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
bfa_fcs_fabric_delete(fabric);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* Authentication failed
*/
static void
bfa_fcs_fabric_sm_auth_failed(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
bfa_fcs_fabric_notify_offline(fabric);
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
bfa_fcs_fabric_delete(fabric);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* Port is in loopback mode.
*/
static void
bfa_fcs_fabric_sm_loopback(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
bfa_fcs_fabric_notify_offline(fabric);
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
bfa_fcs_fabric_delete(fabric);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* There is no attached fabric - private loop or NPort-to-NPort topology.
*/
static void
bfa_fcs_fabric_sm_nofabric(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
bfa_lps_discard(fabric->lps);
bfa_fcs_fabric_notify_offline(fabric);
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
bfa_fcs_fabric_delete(fabric);
break;
case BFA_FCS_FABRIC_SM_NO_FABRIC:
bfa_trc(fabric->fcs, fabric->bb_credit);
bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* Fabric is online - normal operating state.
*/
static void
bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
bfa_lps_discard(fabric->lps);
bfa_fcs_fabric_notify_offline(fabric);
break;
case BFA_FCS_FABRIC_SM_DELETE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
bfa_fcs_fabric_delete(fabric);
break;
case BFA_FCS_FABRIC_SM_AUTH_FAILED:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth_failed);
bfa_lps_discard(fabric->lps);
break;
case BFA_FCS_FABRIC_SM_AUTH_SUCCESS:
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* Exchanging virtual fabric parameters.
*/
static void
bfa_fcs_fabric_sm_evfp(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_CONT_OP:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_evfp_done);
break;
case BFA_FCS_FABRIC_SM_ISOLATE:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_isolated);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* EVFP exchange complete and VFT tagging is enabled.
*/
static void
bfa_fcs_fabric_sm_evfp_done(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
}
/**
* Port is isolated after EVFP exchange due to VF_ID mismatch (N and F).
*/
static void
bfa_fcs_fabric_sm_isolated(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
bfa_log(fabric->fcs->logm, BFA_LOG_FCS_FABRIC_ISOLATED,
fabric->bport.port_cfg.pwwn, fabric->fcs->port_vfid,
fabric->event_arg.swp_vfid);
}
/**
* Fabric is being deleted, awaiting vport delete completions.
*/
static void
bfa_fcs_fabric_sm_deleting(struct bfa_fcs_fabric_s *fabric,
enum bfa_fcs_fabric_event event)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, event);
switch (event) {
case BFA_FCS_FABRIC_SM_DELCOMP:
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit);
bfa_fcs_modexit_comp(fabric->fcs);
break;
case BFA_FCS_FABRIC_SM_LINK_UP:
break;
case BFA_FCS_FABRIC_SM_LINK_DOWN:
bfa_fcs_fabric_notify_offline(fabric);
break;
default:
bfa_sm_fault(fabric->fcs, event);
}
}
/**
* fcs_fabric_private fabric private functions
*/
static void
bfa_fcs_fabric_init(struct bfa_fcs_fabric_s *fabric)
{
struct bfa_port_cfg_s *port_cfg = &fabric->bport.port_cfg;
port_cfg->roles = BFA_PORT_ROLE_FCP_IM;
port_cfg->nwwn = bfa_ioc_get_nwwn(&fabric->fcs->bfa->ioc);
port_cfg->pwwn = bfa_ioc_get_pwwn(&fabric->fcs->bfa->ioc);
}
/**
* Port Symbolic Name Creation for base port.
*/
void
bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric)
{
struct bfa_port_cfg_s *port_cfg = &fabric->bport.port_cfg;
struct bfa_adapter_attr_s adapter_attr;
struct bfa_fcs_driver_info_s *driver_info = &fabric->fcs->driver_info;
bfa_os_memset((void *)&adapter_attr, 0,
sizeof(struct bfa_adapter_attr_s));
bfa_ioc_get_adapter_attr(&fabric->fcs->bfa->ioc, &adapter_attr);
/*
* Model name/number
*/
strncpy((char *)&port_cfg->sym_name, adapter_attr.model,
BFA_FCS_PORT_SYMBNAME_MODEL_SZ);
strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
/*
* Driver Version
*/
strncat((char *)&port_cfg->sym_name, (char *)driver_info->version,
BFA_FCS_PORT_SYMBNAME_VERSION_SZ);
strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
/*
* Host machine name
*/
strncat((char *)&port_cfg->sym_name,
(char *)driver_info->host_machine_name,
BFA_FCS_PORT_SYMBNAME_MACHINENAME_SZ);
strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
/*
* Host OS Info :
* If OS Patch Info is not there, do not truncate any bytes from the
* OS name string and instead copy the entire OS info string (64 bytes).
*/
if (driver_info->host_os_patch[0] == '\0') {
strncat((char *)&port_cfg->sym_name,
(char *)driver_info->host_os_name, BFA_FCS_OS_STR_LEN);
strncat((char *)&port_cfg->sym_name,
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
} else {
strncat((char *)&port_cfg->sym_name,
(char *)driver_info->host_os_name,
BFA_FCS_PORT_SYMBNAME_OSINFO_SZ);
strncat((char *)&port_cfg->sym_name,
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR));
/*
* Append host OS Patch Info
*/
strncat((char *)&port_cfg->sym_name,
(char *)driver_info->host_os_patch,
BFA_FCS_PORT_SYMBNAME_OSPATCH_SZ);
}
/*
* null terminate
*/
port_cfg->sym_name.symname[BFA_SYMNAME_MAXLEN - 1] = 0;
}
/**
* bfa lps login completion callback
*/
void
bfa_cb_lps_flogi_comp(void *bfad, void *uarg, bfa_status_t status)
{
struct bfa_fcs_fabric_s *fabric = uarg;
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_trc(fabric->fcs, status);
switch (status) {
case BFA_STATUS_OK:
fabric->stats.flogi_accepts++;
break;
case BFA_STATUS_INVALID_MAC:
/*
* Only for CNA
*/
fabric->stats.flogi_acc_err++;
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP);
return;
case BFA_STATUS_EPROTOCOL:
switch (bfa_lps_get_extstatus(fabric->lps)) {
case BFA_EPROTO_BAD_ACCEPT:
fabric->stats.flogi_acc_err++;
break;
case BFA_EPROTO_UNKNOWN_RSP:
fabric->stats.flogi_unknown_rsp++;
break;
default:
break;
}
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP);
return;
case BFA_STATUS_FABRIC_RJT:
fabric->stats.flogi_rejects++;
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP);
return;
default:
fabric->stats.flogi_rsp_err++;
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP);
return;
}
fabric->bb_credit = bfa_lps_get_peer_bbcredit(fabric->lps);
bfa_trc(fabric->fcs, fabric->bb_credit);
if (!bfa_lps_is_brcd_fabric(fabric->lps))
fabric->fabric_name = bfa_lps_get_peer_nwwn(fabric->lps);
/*
* Check port type. It should be 1 = F-port.
*/
if (bfa_lps_is_fport(fabric->lps)) {
fabric->bport.pid = bfa_lps_get_pid(fabric->lps);
fabric->is_npiv = bfa_lps_is_npiv_en(fabric->lps);
fabric->is_auth = bfa_lps_is_authreq(fabric->lps);
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_CONT_OP);
} else {
/*
* Nport-2-Nport direct attached
*/
fabric->bport.port_topo.pn2n.rem_port_wwn =
bfa_lps_get_peer_pwwn(fabric->lps);
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_NO_FABRIC);
}
bfa_trc(fabric->fcs, fabric->bport.pid);
bfa_trc(fabric->fcs, fabric->is_npiv);
bfa_trc(fabric->fcs, fabric->is_auth);
}
/**
* Allocate and send FLOGI.
*/
static void
bfa_fcs_fabric_login(struct bfa_fcs_fabric_s *fabric)
{
struct bfa_s *bfa = fabric->fcs->bfa;
struct bfa_port_cfg_s *pcfg = &fabric->bport.port_cfg;
u8 alpa = 0;
if (bfa_pport_get_topology(bfa) == BFA_PPORT_TOPOLOGY_LOOP)
alpa = bfa_pport_get_myalpa(bfa);
bfa_lps_flogi(fabric->lps, fabric, alpa, bfa_pport_get_maxfrsize(bfa),
pcfg->pwwn, pcfg->nwwn, fabric->auth_reqd);
fabric->stats.flogi_sent++;
}
static void
bfa_fcs_fabric_notify_online(struct bfa_fcs_fabric_s *fabric)
{
struct bfa_fcs_vport_s *vport;
struct list_head *qe, *qen;
bfa_trc(fabric->fcs, fabric->fabric_name);
bfa_fcs_fabric_set_opertype(fabric);
fabric->stats.fabric_onlines++;
/**
* notify online event to base and then virtual ports
*/
bfa_fcs_port_online(&fabric->bport);
list_for_each_safe(qe, qen, &fabric->vport_q) {
vport = (struct bfa_fcs_vport_s *)qe;
bfa_fcs_vport_online(vport);
}
}
static void
bfa_fcs_fabric_notify_offline(struct bfa_fcs_fabric_s *fabric)
{
struct bfa_fcs_vport_s *vport;
struct list_head *qe, *qen;
bfa_trc(fabric->fcs, fabric->fabric_name);
fabric->stats.fabric_offlines++;
/**
* notify offline event first to vports and then base port.
*/
list_for_each_safe(qe, qen, &fabric->vport_q) {
vport = (struct bfa_fcs_vport_s *)qe;
bfa_fcs_vport_offline(vport);
}
bfa_fcs_port_offline(&fabric->bport);
fabric->fabric_name = 0;
fabric->fabric_ip_addr[0] = 0;
}
static void
bfa_fcs_fabric_delay(void *cbarg)
{
struct bfa_fcs_fabric_s *fabric = cbarg;
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELAYED);
}
/**
* Delete all vports and wait for vport delete completions.
*/
static void
bfa_fcs_fabric_delete(struct bfa_fcs_fabric_s *fabric)
{
struct bfa_fcs_vport_s *vport;
struct list_head *qe, *qen;
list_for_each_safe(qe, qen, &fabric->vport_q) {
vport = (struct bfa_fcs_vport_s *)qe;
bfa_fcs_vport_delete(vport);
}
bfa_fcs_port_delete(&fabric->bport);
bfa_wc_wait(&fabric->wc);
}
static void
bfa_fcs_fabric_delete_comp(void *cbarg)
{
struct bfa_fcs_fabric_s *fabric = cbarg;
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELCOMP);
}
/**
* fcs_fabric_public fabric public functions
*/
/**
* Attach time initialization
*/
void
bfa_fcs_fabric_attach(struct bfa_fcs_s *fcs)
{
struct bfa_fcs_fabric_s *fabric;
fabric = &fcs->fabric;
bfa_os_memset(fabric, 0, sizeof(struct bfa_fcs_fabric_s));
/**
* Initialize base fabric.
*/
fabric->fcs = fcs;
INIT_LIST_HEAD(&fabric->vport_q);
INIT_LIST_HEAD(&fabric->vf_q);
fabric->lps = bfa_lps_alloc(fcs->bfa);
bfa_assert(fabric->lps);
/**
* Initialize fabric delete completion handler. Fabric deletion is complete
* when the last vport delete is complete.
*/
bfa_wc_init(&fabric->wc, bfa_fcs_fabric_delete_comp, fabric);
bfa_wc_up(&fabric->wc); /* For the base port */
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit);
bfa_fcs_lport_attach(&fabric->bport, fabric->fcs, FC_VF_ID_NULL, NULL);
}
void
bfa_fcs_fabric_modinit(struct bfa_fcs_s *fcs)
{
bfa_sm_send_event(&fcs->fabric, BFA_FCS_FABRIC_SM_CREATE);
bfa_trc(fcs, 0);
}
/**
* Module cleanup
*/
void
bfa_fcs_fabric_modexit(struct bfa_fcs_s *fcs)
{
struct bfa_fcs_fabric_s *fabric;
bfa_trc(fcs, 0);
/**
* Cleanup base fabric.
*/
fabric = &fcs->fabric;
bfa_lps_delete(fabric->lps);
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELETE);
}
/**
* Fabric module start -- kick starts FCS actions
*/
void
bfa_fcs_fabric_modstart(struct bfa_fcs_s *fcs)
{
struct bfa_fcs_fabric_s *fabric;
bfa_trc(fcs, 0);
fabric = &fcs->fabric;
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_START);
}
/**
* Suspend fabric activity as part of driver suspend.
*/
void
bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs)
{
}
bfa_boolean_t
bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric)
{
return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback);
}
enum bfa_pport_type
bfa_fcs_fabric_port_type(struct bfa_fcs_fabric_s *fabric)
{
return fabric->oper_type;
}
/**
* Link up notification from BFA physical port module.
*/
void
bfa_fcs_fabric_link_up(struct bfa_fcs_fabric_s *fabric)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LINK_UP);
}
/**
* Link down notification from BFA physical port module.
*/
void
bfa_fcs_fabric_link_down(struct bfa_fcs_fabric_s *fabric)
{
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LINK_DOWN);
}
/**
* A child vport is being created in the fabric.
*
* Call from vport module at vport creation. A list of base port and vports
* belonging to a fabric is maintained to propagate link events.
*
* param[in] fabric - Fabric instance. This can be a base fabric or vf.
* param[in] vport - Vport being created.
*
* @return None (always succeeds)
*/
void
bfa_fcs_fabric_addvport(struct bfa_fcs_fabric_s *fabric,
struct bfa_fcs_vport_s *vport)
{
/**
* - add vport to fabric's vport_q
*/
bfa_trc(fabric->fcs, fabric->vf_id);
list_add_tail(&vport->qe, &fabric->vport_q);
fabric->num_vports++;
bfa_wc_up(&fabric->wc);
}
/**
* A child vport is being deleted from fabric.
*
* Vport is being deleted.
*/
void
bfa_fcs_fabric_delvport(struct bfa_fcs_fabric_s *fabric,
struct bfa_fcs_vport_s *vport)
{
list_del(&vport->qe);
fabric->num_vports--;
bfa_wc_down(&fabric->wc);
}
/**
* Base port is deleted.
*/
void
bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric)
{
bfa_wc_down(&fabric->wc);
}
/**
* Check if fabric is online.
*
* param[in] fabric - Fabric instance. This can be a base fabric or vf.
*
* @return TRUE/FALSE
*/
int
bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric)
{
return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online);
}
bfa_status_t
bfa_fcs_fabric_addvf(struct bfa_fcs_fabric_s *vf, struct bfa_fcs_s *fcs,
struct bfa_port_cfg_s *port_cfg,
struct bfad_vf_s *vf_drv)
{
bfa_sm_set_state(vf, bfa_fcs_fabric_sm_uninit);
return BFA_STATUS_OK;
}
/**
* Lookup for a vport withing a fabric given its pwwn
*/
struct bfa_fcs_vport_s *
bfa_fcs_fabric_vport_lookup(struct bfa_fcs_fabric_s *fabric, wwn_t pwwn)
{
struct bfa_fcs_vport_s *vport;
struct list_head *qe;
list_for_each(qe, &fabric->vport_q) {
vport = (struct bfa_fcs_vport_s *)qe;
if (bfa_fcs_port_get_pwwn(&vport->lport) == pwwn)
return vport;
}
return NULL;
}
/**
* In a given fabric, return the number of lports.
*
* param[in] fabric - Fabric instance. This can be a base fabric or vf.
*
* @return : 1 or more.
*/
u16
bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric)
{
return fabric->num_vports;
}
/**
* Unsolicited frame receive handling.
*/
void
bfa_fcs_fabric_uf_recv(struct bfa_fcs_fabric_s *fabric, struct fchs_s *fchs,
u16 len)
{
u32 pid = fchs->d_id;
struct bfa_fcs_vport_s *vport;
struct list_head *qe;
struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1);
struct fc_logi_s *flogi = (struct fc_logi_s *) els_cmd;
bfa_trc(fabric->fcs, len);
bfa_trc(fabric->fcs, pid);
/**
* Look for our own FLOGI frames being looped back. This means an
* external loopback cable is in place. Our own FLOGI frames are
* sometimes looped back when switch port gets temporarily bypassed.
*/
if ((pid == bfa_os_ntoh3b(FC_FABRIC_PORT))
&& (els_cmd->els_code == FC_ELS_FLOGI)
&& (flogi->port_name == bfa_fcs_port_get_pwwn(&fabric->bport))) {
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LOOPBACK);
return;
}
/**
* FLOGI/EVFP exchanges should be consumed by base fabric.
*/
if (fchs->d_id == bfa_os_hton3b(FC_FABRIC_PORT)) {
bfa_trc(fabric->fcs, pid);
bfa_fcs_fabric_process_uf(fabric, fchs, len);
return;
}
if (fabric->bport.pid == pid) {
/**
* All authentication frames should be routed to auth
*/
bfa_trc(fabric->fcs, els_cmd->els_code);
if (els_cmd->els_code == FC_ELS_AUTH) {
bfa_trc(fabric->fcs, els_cmd->els_code);
fabric->auth.response = (u8 *) els_cmd;
return;
}
bfa_trc(fabric->fcs, *(u8 *) ((u8 *) fchs));
bfa_fcs_port_uf_recv(&fabric->bport, fchs, len);
return;
}
/**
* look for a matching local port ID
*/
list_for_each(qe, &fabric->vport_q) {
vport = (struct bfa_fcs_vport_s *)qe;
if (vport->lport.pid == pid) {
bfa_fcs_port_uf_recv(&vport->lport, fchs, len);
return;
}
}
bfa_trc(fabric->fcs, els_cmd->els_code);
bfa_fcs_port_uf_recv(&fabric->bport, fchs, len);
}
/**
* Unsolicited frames to be processed by fabric.
*/
static void
bfa_fcs_fabric_process_uf(struct bfa_fcs_fabric_s *fabric, struct fchs_s *fchs,
u16 len)
{
struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1);
bfa_trc(fabric->fcs, els_cmd->els_code);
switch (els_cmd->els_code) {
case FC_ELS_FLOGI:
bfa_fcs_fabric_process_flogi(fabric, fchs, len);
break;
default:
/*
* need to generate a LS_RJT
*/
break;
}
}
/**
* Process incoming FLOGI
*/
static void
bfa_fcs_fabric_process_flogi(struct bfa_fcs_fabric_s *fabric,
struct fchs_s *fchs, u16 len)
{
struct fc_logi_s *flogi = (struct fc_logi_s *) (fchs + 1);
struct bfa_fcs_port_s *bport = &fabric->bport;
bfa_trc(fabric->fcs, fchs->s_id);
fabric->stats.flogi_rcvd++;
/*
* Check port type. It should be 0 = n-port.
*/
if (flogi->csp.port_type) {
/*
* @todo: may need to send a LS_RJT
*/
bfa_trc(fabric->fcs, flogi->port_name);
fabric->stats.flogi_rejected++;
return;
}
fabric->bb_credit = bfa_os_ntohs(flogi->csp.bbcred);
bport->port_topo.pn2n.rem_port_wwn = flogi->port_name;
bport->port_topo.pn2n.reply_oxid = fchs->ox_id;
/*
* Send a Flogi Acc
*/
bfa_fcs_fabric_send_flogi_acc(fabric);
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_NO_FABRIC);
}
static void
bfa_fcs_fabric_send_flogi_acc(struct bfa_fcs_fabric_s *fabric)
{
struct bfa_port_cfg_s *pcfg = &fabric->bport.port_cfg;
struct bfa_fcs_port_n2n_s *n2n_port = &fabric->bport.port_topo.pn2n;
struct bfa_s *bfa = fabric->fcs->bfa;
struct bfa_fcxp_s *fcxp;
u16 reqlen;
struct fchs_s fchs;
fcxp = bfa_fcs_fcxp_alloc(fabric->fcs);
/**
* Do not expect this failure -- expect remote node to retry
*/
if (!fcxp)
return;
reqlen = fc_flogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
bfa_os_hton3b(FC_FABRIC_PORT),
n2n_port->reply_oxid, pcfg->pwwn,
pcfg->nwwn, bfa_pport_get_maxfrsize(bfa),
bfa_pport_get_rx_bbcredit(bfa));
bfa_fcxp_send(fcxp, NULL, fabric->vf_id, bfa_lps_get_tag(fabric->lps),
BFA_FALSE, FC_CLASS_3, reqlen, &fchs,
bfa_fcs_fabric_flogiacc_comp, fabric,
FC_MAX_PDUSZ, 0); /* Timeout 0 indicates no
* response expected
*/
}
/**
* Flogi Acc completion callback.
*/
static void
bfa_fcs_fabric_flogiacc_comp(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
bfa_status_t status, u32 rsp_len,
u32 resid_len, struct fchs_s *rspfchs)
{
struct bfa_fcs_fabric_s *fabric = cbarg;
bfa_trc(fabric->fcs, status);
}
/*
*
* @param[in] fabric - fabric
* @param[in] result - 1
*
* @return - none
*/
void
bfa_fcs_auth_finished(struct bfa_fcs_fabric_s *fabric, enum auth_status status)
{
bfa_trc(fabric->fcs, status);
if (status == FC_AUTH_STATE_SUCCESS)
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_AUTH_SUCCESS);
else
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_AUTH_FAILED);
}
/**
* Send AEN notification
*/
static void
bfa_fcs_fabric_aen_post(struct bfa_fcs_port_s *port,
enum bfa_port_aen_event event)
{
union bfa_aen_data_u aen_data;
struct bfa_log_mod_s *logmod = port->fcs->logm;
wwn_t pwwn = bfa_fcs_port_get_pwwn(port);
wwn_t fwwn = bfa_fcs_port_get_fabric_name(port);
char pwwn_ptr[BFA_STRING_32];
char fwwn_ptr[BFA_STRING_32];
wwn2str(pwwn_ptr, pwwn);
wwn2str(fwwn_ptr, fwwn);
switch (event) {
case BFA_PORT_AEN_FABRIC_NAME_CHANGE:
bfa_log(logmod, BFA_AEN_PORT_FABRIC_NAME_CHANGE, pwwn_ptr,
fwwn_ptr);
break;
default:
break;
}
aen_data.port.pwwn = pwwn;
aen_data.port.fwwn = fwwn;
}
/*
*
* @param[in] fabric - fabric
* @param[in] wwn_t - new fabric name
*
* @return - none
*/
void
bfa_fcs_fabric_set_fabric_name(struct bfa_fcs_fabric_s *fabric,
wwn_t fabric_name)
{
bfa_trc(fabric->fcs, fabric_name);
if (fabric->fabric_name == 0) {
/*
* With BRCD switches, we don't get Fabric Name in FLOGI.
* Don't generate a fabric name change event in this case.
*/
fabric->fabric_name = fabric_name;
} else {
fabric->fabric_name = fabric_name;
/*
* Generate a Event
*/
bfa_fcs_fabric_aen_post(&fabric->bport,
BFA_PORT_AEN_FABRIC_NAME_CHANGE);
}
}
/**
* Not used by FCS.
*/
void
bfa_cb_lps_flogo_comp(void *bfad, void *uarg)
{
}