forked from Minki/linux
52fa7bf9ea
Based on 1 normalized pattern(s): 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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 66 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190529141901.606369721@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1617 lines
39 KiB
C
1617 lines
39 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
|
|
* Copyright (c) 2014- QLogic Corporation.
|
|
* All rights reserved
|
|
* www.qlogic.com
|
|
*
|
|
* Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
|
|
*/
|
|
|
|
/*
|
|
* bfa_fcs.c BFA FCS main
|
|
*/
|
|
|
|
#include "bfad_drv.h"
|
|
#include "bfad_im.h"
|
|
#include "bfa_fcs.h"
|
|
#include "bfa_fcbuild.h"
|
|
|
|
BFA_TRC_FILE(FCS, FCS);
|
|
|
|
/*
|
|
* fcs_api BFA FCS API
|
|
*/
|
|
|
|
static void
|
|
bfa_fcs_exit_comp(void *fcs_cbarg)
|
|
{
|
|
struct bfa_fcs_s *fcs = fcs_cbarg;
|
|
struct bfad_s *bfad = fcs->bfad;
|
|
|
|
complete(&bfad->comp);
|
|
}
|
|
|
|
/*
|
|
* fcs initialization, called once after bfa initialization is complete
|
|
*/
|
|
void
|
|
bfa_fcs_init(struct bfa_fcs_s *fcs)
|
|
{
|
|
bfa_sm_send_event(&fcs->fabric, BFA_FCS_FABRIC_SM_CREATE);
|
|
bfa_trc(fcs, 0);
|
|
}
|
|
|
|
/*
|
|
* fcs_api BFA FCS API
|
|
*/
|
|
|
|
/*
|
|
* FCS update cfg - reset the pwwn/nwwn of fabric base logical port
|
|
* with values learned during bfa_init firmware GETATTR REQ.
|
|
*/
|
|
void
|
|
bfa_fcs_update_cfg(struct bfa_fcs_s *fcs)
|
|
{
|
|
struct bfa_fcs_fabric_s *fabric = &fcs->fabric;
|
|
struct bfa_lport_cfg_s *port_cfg = &fabric->bport.port_cfg;
|
|
struct bfa_ioc_s *ioc = &fabric->fcs->bfa->ioc;
|
|
|
|
port_cfg->nwwn = ioc->attr->nwwn;
|
|
port_cfg->pwwn = ioc->attr->pwwn;
|
|
}
|
|
|
|
/*
|
|
* Stop FCS operations.
|
|
*/
|
|
void
|
|
bfa_fcs_stop(struct bfa_fcs_s *fcs)
|
|
{
|
|
bfa_wc_init(&fcs->wc, bfa_fcs_exit_comp, fcs);
|
|
bfa_wc_up(&fcs->wc);
|
|
bfa_fcs_fabric_modstop(fcs);
|
|
bfa_wc_wait(&fcs->wc);
|
|
}
|
|
|
|
/*
|
|
* fcs pbc vport initialization
|
|
*/
|
|
void
|
|
bfa_fcs_pbc_vport_init(struct bfa_fcs_s *fcs)
|
|
{
|
|
int i, npbc_vports;
|
|
struct bfi_pbc_vport_s pbc_vports[BFI_PBC_MAX_VPORTS];
|
|
|
|
/* Initialize pbc vports */
|
|
if (!fcs->min_cfg) {
|
|
npbc_vports =
|
|
bfa_iocfc_get_pbc_vports(fcs->bfa, pbc_vports);
|
|
for (i = 0; i < npbc_vports; i++)
|
|
bfa_fcb_pbc_vport_create(fcs->bfa->bfad, pbc_vports[i]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* brief
|
|
* FCS driver details initialization.
|
|
*
|
|
* param[in] fcs FCS instance
|
|
* param[in] driver_info Driver Details
|
|
*
|
|
* return None
|
|
*/
|
|
void
|
|
bfa_fcs_driver_info_init(struct bfa_fcs_s *fcs,
|
|
struct bfa_fcs_driver_info_s *driver_info)
|
|
{
|
|
|
|
fcs->driver_info = *driver_info;
|
|
|
|
bfa_fcs_fabric_psymb_init(&fcs->fabric);
|
|
bfa_fcs_fabric_nsymb_init(&fcs->fabric);
|
|
}
|
|
|
|
/*
|
|
* brief
|
|
* FCS instance cleanup and exit.
|
|
*
|
|
* param[in] fcs FCS instance
|
|
* return None
|
|
*/
|
|
void
|
|
bfa_fcs_exit(struct bfa_fcs_s *fcs)
|
|
{
|
|
bfa_wc_init(&fcs->wc, bfa_fcs_exit_comp, fcs);
|
|
bfa_wc_up(&fcs->wc);
|
|
bfa_trc(fcs, 0);
|
|
bfa_lps_delete(fcs->fabric.lps);
|
|
bfa_sm_send_event(&fcs->fabric, BFA_FCS_FABRIC_SM_DELETE);
|
|
bfa_wc_wait(&fcs->wc);
|
|
}
|
|
|
|
/*
|
|
* Fabric module implementation.
|
|
*/
|
|
|
|
#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_fcport_get_topology((__fabric)->fcs->bfa) \
|
|
== BFA_PORT_TOPOLOGY_P2P) { \
|
|
if (fabric->fab_type == BFA_FCS_FABRIC_SWITCHED) \
|
|
(__fabric)->oper_type = BFA_PORT_TYPE_NPORT; \
|
|
else \
|
|
(__fabric)->oper_type = BFA_PORT_TYPE_P2P; \
|
|
} else \
|
|
(__fabric)->oper_type = BFA_PORT_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_stop(struct bfa_fcs_fabric_s *fabric);
|
|
static void bfa_fcs_fabric_stop_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);
|
|
|
|
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_nofabric(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);
|
|
static void bfa_fcs_fabric_sm_stopping(struct bfa_fcs_fabric_s *fabric,
|
|
enum bfa_fcs_fabric_event event);
|
|
static void bfa_fcs_fabric_sm_cleanup(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)
|
|
{
|
|
struct bfa_s *bfa = fabric->fcs->bfa;
|
|
|
|
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
|
|
bfa_trc(fabric->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_FABRIC_SM_START:
|
|
if (!bfa_fcport_is_linkup(fabric->fcs->bfa)) {
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown);
|
|
break;
|
|
}
|
|
if (bfa_fcport_get_topology(bfa) ==
|
|
BFA_PORT_TOPOLOGY_LOOP) {
|
|
fabric->fab_type = BFA_FCS_FABRIC_LOOP;
|
|
fabric->bport.pid = bfa_fcport_get_myalpa(bfa);
|
|
fabric->bport.pid = bfa_hton3b(fabric->bport.pid);
|
|
bfa_sm_set_state(fabric,
|
|
bfa_fcs_fabric_sm_online);
|
|
bfa_fcs_fabric_set_opertype(fabric);
|
|
bfa_fcs_lport_online(&fabric->bport);
|
|
} else {
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi);
|
|
bfa_fcs_fabric_login(fabric);
|
|
}
|
|
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_deleting);
|
|
bfa_fcs_fabric_delete(fabric);
|
|
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)
|
|
{
|
|
struct bfa_s *bfa = fabric->fcs->bfa;
|
|
|
|
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
|
|
bfa_trc(fabric->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_FABRIC_SM_LINK_UP:
|
|
if (bfa_fcport_get_topology(bfa) != BFA_PORT_TOPOLOGY_LOOP) {
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi);
|
|
bfa_fcs_fabric_login(fabric);
|
|
break;
|
|
}
|
|
fabric->fab_type = BFA_FCS_FABRIC_LOOP;
|
|
fabric->bport.pid = bfa_fcport_get_myalpa(bfa);
|
|
fabric->bport.pid = bfa_hton3b(fabric->bport.pid);
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online);
|
|
bfa_fcs_fabric_set_opertype(fabric);
|
|
bfa_fcs_lport_online(&fabric->bport);
|
|
break;
|
|
|
|
case BFA_FCS_FABRIC_SM_RETRY_OP:
|
|
case BFA_FCS_FABRIC_SM_LOOPBACK:
|
|
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_STOP:
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup);
|
|
bfa_fcs_fabric_stop(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_fcport_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_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
|
|
bfa_fcs_fabric_set_opertype(fabric);
|
|
break;
|
|
|
|
case BFA_FCS_FABRIC_SM_NO_FABRIC:
|
|
fabric->fab_type = BFA_FCS_FABRIC_N2N;
|
|
bfa_fcport_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_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
|
|
break;
|
|
|
|
case BFA_FCS_FABRIC_SM_DELETE:
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting);
|
|
bfa_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
|
|
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_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
|
|
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_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
|
|
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
|
|
*/
|
|
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.
|
|
*/
|
|
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_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
|
|
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_fcport_set_tx_bbcredit(fabric->fcs->bfa,
|
|
fabric->bb_credit);
|
|
break;
|
|
|
|
case BFA_FCS_FABRIC_SM_RETRY_OP:
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(fabric->fcs, event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fabric is online - normal operating state.
|
|
*/
|
|
void
|
|
bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric,
|
|
enum bfa_fcs_fabric_event event)
|
|
{
|
|
struct bfa_s *bfa = fabric->fcs->bfa;
|
|
|
|
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);
|
|
if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) {
|
|
bfa_fcs_lport_offline(&fabric->bport);
|
|
} else {
|
|
bfa_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
|
|
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_STOP:
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_stopping);
|
|
bfa_fcs_fabric_stop(fabric);
|
|
break;
|
|
|
|
case BFA_FCS_FABRIC_SM_AUTH_FAILED:
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth_failed);
|
|
bfa_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE);
|
|
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)
|
|
{
|
|
struct bfad_s *bfad = (struct bfad_s *)fabric->fcs->bfad;
|
|
char pwwn_ptr[BFA_STRING_32];
|
|
|
|
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
|
|
bfa_trc(fabric->fcs, event);
|
|
wwn2str(pwwn_ptr, fabric->bport.port_cfg.pwwn);
|
|
|
|
BFA_LOG(KERN_INFO, bfad, bfa_log_level,
|
|
"Port is isolated due to VF_ID mismatch. "
|
|
"PWWN: %s Port VF_ID: %04x switch port VF_ID: %04x.",
|
|
pwwn_ptr, 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_wc_down(&fabric->fcs->wc);
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fabric is being stopped, awaiting vport stop completions.
|
|
*/
|
|
static void
|
|
bfa_fcs_fabric_sm_stopping(struct bfa_fcs_fabric_s *fabric,
|
|
enum bfa_fcs_fabric_event event)
|
|
{
|
|
struct bfa_s *bfa = fabric->fcs->bfa;
|
|
|
|
bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn);
|
|
bfa_trc(fabric->fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_FCS_FABRIC_SM_STOPCOMP:
|
|
if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) {
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created);
|
|
} else {
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup);
|
|
bfa_sm_send_event(fabric->lps, BFA_LPS_SM_LOGOUT);
|
|
}
|
|
break;
|
|
|
|
case BFA_FCS_FABRIC_SM_LINK_UP:
|
|
break;
|
|
|
|
case BFA_FCS_FABRIC_SM_LINK_DOWN:
|
|
if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP)
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created);
|
|
else
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_fault(fabric->fcs, event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fabric is being stopped, cleanup without FLOGO
|
|
*/
|
|
static void
|
|
bfa_fcs_fabric_sm_cleanup(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_STOPCOMP:
|
|
case BFA_FCS_FABRIC_SM_LOGOCOMP:
|
|
bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created);
|
|
bfa_wc_down(&(fabric->fcs)->wc);
|
|
break;
|
|
|
|
case BFA_FCS_FABRIC_SM_LINK_DOWN:
|
|
/*
|
|
* Ignore - can get this event if we get notified about IOC down
|
|
* before the fabric completion callbk is done.
|
|
*/
|
|
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_lport_cfg_s *port_cfg = &fabric->bport.port_cfg;
|
|
|
|
port_cfg->roles = BFA_LPORT_ROLE_FCP_IM;
|
|
port_cfg->nwwn = fabric->fcs->bfa->ioc.attr->nwwn;
|
|
port_cfg->pwwn = fabric->fcs->bfa->ioc.attr->pwwn;
|
|
}
|
|
|
|
/*
|
|
* Port Symbolic Name Creation for base port.
|
|
*/
|
|
void
|
|
bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric)
|
|
{
|
|
struct bfa_lport_cfg_s *port_cfg = &fabric->bport.port_cfg;
|
|
char model[BFA_ADAPTER_MODEL_NAME_LEN] = {0};
|
|
struct bfa_fcs_driver_info_s *driver_info = &fabric->fcs->driver_info;
|
|
|
|
bfa_ioc_get_adapter_model(&fabric->fcs->bfa->ioc, model);
|
|
|
|
/* Model name/number */
|
|
strlcpy(port_cfg->sym_name.symname, model,
|
|
BFA_SYMNAME_MAXLEN);
|
|
strlcat(port_cfg->sym_name.symname, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
|
|
BFA_SYMNAME_MAXLEN);
|
|
|
|
/* Driver Version */
|
|
strlcat(port_cfg->sym_name.symname, driver_info->version,
|
|
BFA_SYMNAME_MAXLEN);
|
|
strlcat(port_cfg->sym_name.symname, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
|
|
BFA_SYMNAME_MAXLEN);
|
|
|
|
/* Host machine name */
|
|
strlcat(port_cfg->sym_name.symname,
|
|
driver_info->host_machine_name,
|
|
BFA_SYMNAME_MAXLEN);
|
|
strlcat(port_cfg->sym_name.symname, BFA_FCS_PORT_SYMBNAME_SEPARATOR,
|
|
BFA_SYMNAME_MAXLEN);
|
|
|
|
/*
|
|
* 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') {
|
|
strlcat(port_cfg->sym_name.symname,
|
|
driver_info->host_os_name,
|
|
BFA_SYMNAME_MAXLEN);
|
|
strlcat(port_cfg->sym_name.symname,
|
|
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
|
|
BFA_SYMNAME_MAXLEN);
|
|
} else {
|
|
strlcat(port_cfg->sym_name.symname,
|
|
driver_info->host_os_name,
|
|
BFA_SYMNAME_MAXLEN);
|
|
strlcat(port_cfg->sym_name.symname,
|
|
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
|
|
BFA_SYMNAME_MAXLEN);
|
|
|
|
/* Append host OS Patch Info */
|
|
strlcat(port_cfg->sym_name.symname,
|
|
driver_info->host_os_patch,
|
|
BFA_SYMNAME_MAXLEN);
|
|
}
|
|
|
|
/* null terminate */
|
|
port_cfg->sym_name.symname[BFA_SYMNAME_MAXLEN - 1] = 0;
|
|
}
|
|
|
|
/*
|
|
* Node Symbolic Name Creation for base port and all vports
|
|
*/
|
|
void
|
|
bfa_fcs_fabric_nsymb_init(struct bfa_fcs_fabric_s *fabric)
|
|
{
|
|
struct bfa_lport_cfg_s *port_cfg = &fabric->bport.port_cfg;
|
|
char model[BFA_ADAPTER_MODEL_NAME_LEN] = {0};
|
|
struct bfa_fcs_driver_info_s *driver_info = &fabric->fcs->driver_info;
|
|
|
|
bfa_ioc_get_adapter_model(&fabric->fcs->bfa->ioc, model);
|
|
|
|
/* Model name/number */
|
|
strlcpy(port_cfg->node_sym_name.symname, model,
|
|
BFA_SYMNAME_MAXLEN);
|
|
strlcat(port_cfg->node_sym_name.symname,
|
|
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
|
|
BFA_SYMNAME_MAXLEN);
|
|
|
|
/* Driver Version */
|
|
strlcat(port_cfg->node_sym_name.symname, (char *)driver_info->version,
|
|
BFA_SYMNAME_MAXLEN);
|
|
strlcat(port_cfg->node_sym_name.symname,
|
|
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
|
|
BFA_SYMNAME_MAXLEN);
|
|
|
|
/* Host machine name */
|
|
strlcat(port_cfg->node_sym_name.symname,
|
|
driver_info->host_machine_name,
|
|
BFA_SYMNAME_MAXLEN);
|
|
strlcat(port_cfg->node_sym_name.symname,
|
|
BFA_FCS_PORT_SYMBNAME_SEPARATOR,
|
|
BFA_SYMNAME_MAXLEN);
|
|
|
|
/* null terminate */
|
|
port_cfg->node_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 (fabric->lps->ext_status) {
|
|
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 = fabric->lps->pr_bbcred;
|
|
bfa_trc(fabric->fcs, fabric->bb_credit);
|
|
|
|
if (!(fabric->lps->brcd_switch))
|
|
fabric->fabric_name = fabric->lps->pr_nwwn;
|
|
|
|
/*
|
|
* Check port type. It should be 1 = F-port.
|
|
*/
|
|
if (fabric->lps->fport) {
|
|
fabric->bport.pid = fabric->lps->lp_pid;
|
|
fabric->is_npiv = fabric->lps->npiv_en;
|
|
fabric->is_auth = fabric->lps->auth_req;
|
|
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 =
|
|
fabric->lps->pr_pwwn;
|
|
fabric->fab_type = BFA_FCS_FABRIC_N2N;
|
|
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_lport_cfg_s *pcfg = &fabric->bport.port_cfg;
|
|
u8 alpa = 0;
|
|
|
|
|
|
bfa_lps_flogi(fabric->lps, fabric, alpa, bfa_fcport_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_lport_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_lport_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);
|
|
}
|
|
|
|
/*
|
|
* Stop all vports and wait for vport stop completions.
|
|
*/
|
|
static void
|
|
bfa_fcs_fabric_stop(struct bfa_fcs_fabric_s *fabric)
|
|
{
|
|
struct bfa_fcs_vport_s *vport;
|
|
struct list_head *qe, *qen;
|
|
|
|
bfa_wc_init(&fabric->stop_wc, bfa_fcs_fabric_stop_comp, fabric);
|
|
|
|
list_for_each_safe(qe, qen, &fabric->vport_q) {
|
|
vport = (struct bfa_fcs_vport_s *) qe;
|
|
bfa_wc_up(&fabric->stop_wc);
|
|
bfa_fcs_vport_fcs_stop(vport);
|
|
}
|
|
|
|
bfa_wc_up(&fabric->stop_wc);
|
|
bfa_fcs_lport_stop(&fabric->bport);
|
|
bfa_wc_wait(&fabric->stop_wc);
|
|
}
|
|
|
|
/*
|
|
* 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_fcs_delete(vport);
|
|
}
|
|
|
|
bfa_fcs_lport_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);
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_fabric_stop_comp(void *cbarg)
|
|
{
|
|
struct bfa_fcs_fabric_s *fabric = cbarg;
|
|
|
|
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_STOPCOMP);
|
|
}
|
|
|
|
/*
|
|
* fcs_fabric_public fabric public functions
|
|
*/
|
|
|
|
/*
|
|
* Fabric module stop -- stop FCS actions
|
|
*/
|
|
void
|
|
bfa_fcs_fabric_modstop(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_STOP);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
|
|
/*
|
|
* Lookup for a vport within 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_lport_get_pwwn(&vport->lport) == pwwn)
|
|
return vport;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get OUI of the attached switch.
|
|
*
|
|
* Note : Use of this function should be avoided as much as possible.
|
|
* This function should be used only if there is any requirement
|
|
* to check for FOS version below 6.3.
|
|
* To check if the attached fabric is a brocade fabric, use
|
|
* bfa_lps_is_brcd_fabric() which works for FOS versions 6.3
|
|
* or above only.
|
|
*/
|
|
|
|
u16
|
|
bfa_fcs_fabric_get_switch_oui(struct bfa_fcs_fabric_s *fabric)
|
|
{
|
|
wwn_t fab_nwwn;
|
|
u8 *tmp;
|
|
u16 oui;
|
|
|
|
fab_nwwn = fabric->lps->pr_nwwn;
|
|
|
|
tmp = (u8 *)&fab_nwwn;
|
|
oui = (tmp[3] << 8) | tmp[4];
|
|
|
|
return oui;
|
|
}
|
|
/*
|
|
* 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_ntoh3b(FC_FABRIC_PORT)) &&
|
|
(els_cmd->els_code == FC_ELS_FLOGI) &&
|
|
(flogi->port_name == bfa_fcs_lport_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_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);
|
|
return;
|
|
}
|
|
|
|
bfa_trc(fabric->fcs, *(u8 *) ((u8 *) fchs));
|
|
bfa_fcs_lport_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_lport_uf_recv(&vport->lport, fchs, len);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!bfa_fcs_fabric_is_switched(fabric))
|
|
bfa_fcs_lport_uf_recv(&fabric->bport, fchs, len);
|
|
|
|
bfa_trc(fabric->fcs, fchs->type);
|
|
}
|
|
|
|
/*
|
|
* 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_lport_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 = be16_to_cpu(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_lport_cfg_s *pcfg = &fabric->bport.port_cfg;
|
|
struct bfa_fcs_lport_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, BFA_FALSE);
|
|
/*
|
|
* 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_hton3b(FC_FABRIC_PORT),
|
|
n2n_port->reply_oxid, pcfg->pwwn,
|
|
pcfg->nwwn,
|
|
bfa_fcport_get_maxfrsize(bfa),
|
|
bfa_fcport_get_rx_bbcredit(bfa), 0);
|
|
|
|
bfa_fcxp_send(fcxp, NULL, fabric->vf_id, fabric->lps->bfa_tag,
|
|
BFA_FALSE, FC_CLASS_3,
|
|
reqlen, &fchs, bfa_fcs_fabric_flogiacc_comp, fabric,
|
|
FC_MAX_PDUSZ, 0);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
|
|
/*
|
|
* Send AEN notification
|
|
*/
|
|
static void
|
|
bfa_fcs_fabric_aen_post(struct bfa_fcs_lport_s *port,
|
|
enum bfa_port_aen_event event)
|
|
{
|
|
struct bfad_s *bfad = (struct bfad_s *)port->fabric->fcs->bfad;
|
|
struct bfa_aen_entry_s *aen_entry;
|
|
|
|
bfad_get_aen_entry(bfad, aen_entry);
|
|
if (!aen_entry)
|
|
return;
|
|
|
|
aen_entry->aen_data.port.pwwn = bfa_fcs_lport_get_pwwn(port);
|
|
aen_entry->aen_data.port.fwwn = bfa_fcs_lport_get_fabric_name(port);
|
|
|
|
/* Send the AEN notification */
|
|
bfad_im_post_vendor_event(aen_entry, bfad, ++port->fcs->fcs_aen_seq,
|
|
BFA_AEN_CAT_PORT, event);
|
|
}
|
|
|
|
/*
|
|
*
|
|
* @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)
|
|
{
|
|
struct bfad_s *bfad = (struct bfad_s *)fabric->fcs->bfad;
|
|
char pwwn_ptr[BFA_STRING_32];
|
|
char fwwn_ptr[BFA_STRING_32];
|
|
|
|
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;
|
|
wwn2str(pwwn_ptr, bfa_fcs_lport_get_pwwn(&fabric->bport));
|
|
wwn2str(fwwn_ptr,
|
|
bfa_fcs_lport_get_fabric_name(&fabric->bport));
|
|
BFA_LOG(KERN_WARNING, bfad, bfa_log_level,
|
|
"Base port WWN = %s Fabric WWN = %s\n",
|
|
pwwn_ptr, fwwn_ptr);
|
|
bfa_fcs_fabric_aen_post(&fabric->bport,
|
|
BFA_PORT_AEN_FABRIC_NAME_CHANGE);
|
|
}
|
|
}
|
|
|
|
void
|
|
bfa_cb_lps_flogo_comp(void *bfad, void *uarg)
|
|
{
|
|
struct bfa_fcs_fabric_s *fabric = uarg;
|
|
bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LOGOCOMP);
|
|
}
|
|
|
|
/*
|
|
* Returns FCS vf structure for a given vf_id.
|
|
*
|
|
* param[in] vf_id - VF_ID
|
|
*
|
|
* return
|
|
* If lookup succeeds, retuns fcs vf object, otherwise returns NULL
|
|
*/
|
|
bfa_fcs_vf_t *
|
|
bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id)
|
|
{
|
|
bfa_trc(fcs, vf_id);
|
|
if (vf_id == FC_VF_ID_NULL)
|
|
return &fcs->fabric;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Return the list of local logical ports present in the given VF.
|
|
*
|
|
* @param[in] vf vf for which logical ports are returned
|
|
* @param[out] lpwwn returned logical port wwn list
|
|
* @param[in,out] nlports in:size of lpwwn list;
|
|
* out:total elements present,
|
|
* actual elements returned is limited by the size
|
|
*/
|
|
void
|
|
bfa_fcs_vf_get_ports(bfa_fcs_vf_t *vf, wwn_t lpwwn[], int *nlports)
|
|
{
|
|
struct list_head *qe;
|
|
struct bfa_fcs_vport_s *vport;
|
|
int i = 0;
|
|
struct bfa_fcs_s *fcs;
|
|
|
|
if (vf == NULL || lpwwn == NULL || *nlports == 0)
|
|
return;
|
|
|
|
fcs = vf->fcs;
|
|
|
|
bfa_trc(fcs, vf->vf_id);
|
|
bfa_trc(fcs, (uint32_t) *nlports);
|
|
|
|
lpwwn[i++] = vf->bport.port_cfg.pwwn;
|
|
|
|
list_for_each(qe, &vf->vport_q) {
|
|
if (i >= *nlports)
|
|
break;
|
|
|
|
vport = (struct bfa_fcs_vport_s *) qe;
|
|
lpwwn[i++] = vport->lport.port_cfg.pwwn;
|
|
}
|
|
|
|
bfa_trc(fcs, i);
|
|
*nlports = i;
|
|
}
|
|
|
|
/*
|
|
* BFA FCS PPORT ( physical port)
|
|
*/
|
|
static void
|
|
bfa_fcs_port_event_handler(void *cbarg, enum bfa_port_linkstate event)
|
|
{
|
|
struct bfa_fcs_s *fcs = cbarg;
|
|
|
|
bfa_trc(fcs, event);
|
|
|
|
switch (event) {
|
|
case BFA_PORT_LINKUP:
|
|
bfa_fcs_fabric_link_up(&fcs->fabric);
|
|
break;
|
|
|
|
case BFA_PORT_LINKDOWN:
|
|
bfa_fcs_fabric_link_down(&fcs->fabric);
|
|
break;
|
|
|
|
default:
|
|
WARN_ON(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* BFA FCS UF ( Unsolicited Frames)
|
|
*/
|
|
|
|
/*
|
|
* BFA callback for unsolicited frame receive handler.
|
|
*
|
|
* @param[in] cbarg callback arg for receive handler
|
|
* @param[in] uf unsolicited frame descriptor
|
|
*
|
|
* @return None
|
|
*/
|
|
static void
|
|
bfa_fcs_uf_recv(void *cbarg, struct bfa_uf_s *uf)
|
|
{
|
|
struct bfa_fcs_s *fcs = (struct bfa_fcs_s *) cbarg;
|
|
struct fchs_s *fchs = bfa_uf_get_frmbuf(uf);
|
|
u16 len = bfa_uf_get_frmlen(uf);
|
|
struct fc_vft_s *vft;
|
|
struct bfa_fcs_fabric_s *fabric;
|
|
|
|
/*
|
|
* check for VFT header
|
|
*/
|
|
if (fchs->routing == FC_RTG_EXT_HDR &&
|
|
fchs->cat_info == FC_CAT_VFT_HDR) {
|
|
bfa_stats(fcs, uf.tagged);
|
|
vft = bfa_uf_get_frmbuf(uf);
|
|
if (fcs->port_vfid == vft->vf_id)
|
|
fabric = &fcs->fabric;
|
|
else
|
|
fabric = bfa_fcs_vf_lookup(fcs, (u16) vft->vf_id);
|
|
|
|
/*
|
|
* drop frame if vfid is unknown
|
|
*/
|
|
if (!fabric) {
|
|
WARN_ON(1);
|
|
bfa_stats(fcs, uf.vfid_unknown);
|
|
bfa_uf_free(uf);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* skip vft header
|
|
*/
|
|
fchs = (struct fchs_s *) (vft + 1);
|
|
len -= sizeof(struct fc_vft_s);
|
|
|
|
bfa_trc(fcs, vft->vf_id);
|
|
} else {
|
|
bfa_stats(fcs, uf.untagged);
|
|
fabric = &fcs->fabric;
|
|
}
|
|
|
|
bfa_trc(fcs, ((u32 *) fchs)[0]);
|
|
bfa_trc(fcs, ((u32 *) fchs)[1]);
|
|
bfa_trc(fcs, ((u32 *) fchs)[2]);
|
|
bfa_trc(fcs, ((u32 *) fchs)[3]);
|
|
bfa_trc(fcs, ((u32 *) fchs)[4]);
|
|
bfa_trc(fcs, ((u32 *) fchs)[5]);
|
|
bfa_trc(fcs, len);
|
|
|
|
bfa_fcs_fabric_uf_recv(fabric, fchs, len);
|
|
bfa_uf_free(uf);
|
|
}
|
|
|
|
/*
|
|
* fcs attach -- called once to initialize data structures at driver attach time
|
|
*/
|
|
void
|
|
bfa_fcs_attach(struct bfa_fcs_s *fcs, struct bfa_s *bfa, struct bfad_s *bfad,
|
|
bfa_boolean_t min_cfg)
|
|
{
|
|
struct bfa_fcs_fabric_s *fabric = &fcs->fabric;
|
|
|
|
fcs->bfa = bfa;
|
|
fcs->bfad = bfad;
|
|
fcs->min_cfg = min_cfg;
|
|
fcs->num_rport_logins = 0;
|
|
|
|
bfa->fcs = BFA_TRUE;
|
|
fcbuild_init();
|
|
|
|
bfa_fcport_event_register(fcs->bfa, bfa_fcs_port_event_handler, fcs);
|
|
bfa_uf_recv_register(fcs->bfa, bfa_fcs_uf_recv, fcs);
|
|
|
|
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);
|
|
WARN_ON(!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);
|
|
}
|