mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-18 09:44:18 +08:00
7725ccfda5
Add new driver for Brocade Hardware Signed-off-by: Jing Huang <huangj@brocade.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
483 lines
11 KiB
C
483 lines
11 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.
|
|
*/
|
|
|
|
#include <bfa.h>
|
|
#include <bfa_svc.h>
|
|
#include "fcs_lport.h"
|
|
#include "fcs_rport.h"
|
|
#include "fcs_ms.h"
|
|
#include "fcs_trcmod.h"
|
|
#include "fcs_fcxp.h"
|
|
#include "fcs.h"
|
|
#include "lport_priv.h"
|
|
|
|
BFA_TRC_FILE(FCS, SCN);
|
|
|
|
#define FC_QOS_RSCN_EVENT 0x0c
|
|
#define FC_FABRIC_NAME_RSCN_EVENT 0x0d
|
|
|
|
/*
|
|
* forward declarations
|
|
*/
|
|
static void bfa_fcs_port_scn_send_scr(void *scn_cbarg,
|
|
struct bfa_fcxp_s *fcxp_alloced);
|
|
static void bfa_fcs_port_scn_scr_response(void *fcsarg,
|
|
struct bfa_fcxp_s *fcxp,
|
|
void *cbarg,
|
|
bfa_status_t req_status,
|
|
u32 rsp_len,
|
|
u32 resid_len,
|
|
struct fchs_s *rsp_fchs);
|
|
static void bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port,
|
|
struct fchs_s *rx_fchs);
|
|
static void bfa_fcs_port_scn_timeout(void *arg);
|
|
|
|
/**
|
|
* fcs_scm_sm FCS SCN state machine
|
|
*/
|
|
|
|
/**
|
|
* VPort SCN State Machine events
|
|
*/
|
|
enum port_scn_event {
|
|
SCNSM_EVENT_PORT_ONLINE = 1,
|
|
SCNSM_EVENT_PORT_OFFLINE = 2,
|
|
SCNSM_EVENT_RSP_OK = 3,
|
|
SCNSM_EVENT_RSP_ERROR = 4,
|
|
SCNSM_EVENT_TIMEOUT = 5,
|
|
SCNSM_EVENT_SCR_SENT = 6,
|
|
};
|
|
|
|
static void bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event);
|
|
static void bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event);
|
|
static void bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event);
|
|
static void bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event);
|
|
static void bfa_fcs_port_scn_sm_online(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event);
|
|
|
|
/**
|
|
* Starting state - awaiting link up.
|
|
*/
|
|
static void
|
|
bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event)
|
|
{
|
|
switch (event) {
|
|
case SCNSM_EVENT_PORT_ONLINE:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_sending_scr);
|
|
bfa_fcs_port_scn_send_scr(scn, NULL);
|
|
break;
|
|
|
|
case SCNSM_EVENT_PORT_OFFLINE:
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event)
|
|
{
|
|
switch (event) {
|
|
case SCNSM_EVENT_SCR_SENT:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_scr);
|
|
break;
|
|
|
|
case SCNSM_EVENT_PORT_OFFLINE:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline);
|
|
bfa_fcxp_walloc_cancel(scn->port->fcs->bfa, &scn->fcxp_wqe);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event)
|
|
{
|
|
struct bfa_fcs_port_s *port = scn->port;
|
|
|
|
switch (event) {
|
|
case SCNSM_EVENT_RSP_OK:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_online);
|
|
break;
|
|
|
|
case SCNSM_EVENT_RSP_ERROR:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_scr_retry);
|
|
bfa_timer_start(port->fcs->bfa, &scn->timer,
|
|
bfa_fcs_port_scn_timeout, scn,
|
|
BFA_FCS_RETRY_TIMEOUT);
|
|
break;
|
|
|
|
case SCNSM_EVENT_PORT_OFFLINE:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline);
|
|
bfa_fcxp_discard(scn->fcxp);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event)
|
|
{
|
|
switch (event) {
|
|
case SCNSM_EVENT_TIMEOUT:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_sending_scr);
|
|
bfa_fcs_port_scn_send_scr(scn, NULL);
|
|
break;
|
|
|
|
case SCNSM_EVENT_PORT_OFFLINE:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline);
|
|
bfa_timer_stop(&scn->timer);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_port_scn_sm_online(struct bfa_fcs_port_scn_s *scn,
|
|
enum port_scn_event event)
|
|
{
|
|
switch (event) {
|
|
case SCNSM_EVENT_PORT_OFFLINE:
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* fcs_scn_private FCS SCN private functions
|
|
*/
|
|
|
|
/**
|
|
* This routine will be called to send a SCR command.
|
|
*/
|
|
static void
|
|
bfa_fcs_port_scn_send_scr(void *scn_cbarg, struct bfa_fcxp_s *fcxp_alloced)
|
|
{
|
|
struct bfa_fcs_port_scn_s *scn = scn_cbarg;
|
|
struct bfa_fcs_port_s *port = scn->port;
|
|
struct fchs_s fchs;
|
|
int len;
|
|
struct bfa_fcxp_s *fcxp;
|
|
|
|
bfa_trc(port->fcs, port->pid);
|
|
bfa_trc(port->fcs, port->port_cfg.pwwn);
|
|
|
|
fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs);
|
|
if (!fcxp) {
|
|
bfa_fcxp_alloc_wait(port->fcs->bfa, &scn->fcxp_wqe,
|
|
bfa_fcs_port_scn_send_scr, scn);
|
|
return;
|
|
}
|
|
scn->fcxp = fcxp;
|
|
|
|
/*
|
|
* Handle VU registrations for Base port only
|
|
*/
|
|
if ((!port->vport) && bfa_ioc_get_fcmode(&port->fcs->bfa->ioc)) {
|
|
len = fc_scr_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
|
|
bfa_lps_is_brcd_fabric(port->fabric->lps),
|
|
port->pid, 0);
|
|
} else {
|
|
len = fc_scr_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), BFA_FALSE,
|
|
port->pid, 0);
|
|
}
|
|
|
|
bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
|
|
FC_CLASS_3, len, &fchs, bfa_fcs_port_scn_scr_response,
|
|
(void *)scn, FC_MAX_PDUSZ, FC_RA_TOV);
|
|
|
|
bfa_sm_send_event(scn, SCNSM_EVENT_SCR_SENT);
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_port_scn_scr_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
|
|
void *cbarg, bfa_status_t req_status,
|
|
u32 rsp_len, u32 resid_len,
|
|
struct fchs_s *rsp_fchs)
|
|
{
|
|
struct bfa_fcs_port_scn_s *scn = (struct bfa_fcs_port_scn_s *)cbarg;
|
|
struct bfa_fcs_port_s *port = scn->port;
|
|
struct fc_els_cmd_s *els_cmd;
|
|
struct fc_ls_rjt_s *ls_rjt;
|
|
|
|
bfa_trc(port->fcs, port->port_cfg.pwwn);
|
|
|
|
/*
|
|
* Sanity Checks
|
|
*/
|
|
if (req_status != BFA_STATUS_OK) {
|
|
bfa_trc(port->fcs, req_status);
|
|
bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR);
|
|
return;
|
|
}
|
|
|
|
els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp);
|
|
|
|
switch (els_cmd->els_code) {
|
|
|
|
case FC_ELS_ACC:
|
|
bfa_sm_send_event(scn, SCNSM_EVENT_RSP_OK);
|
|
break;
|
|
|
|
case FC_ELS_LS_RJT:
|
|
|
|
ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp);
|
|
|
|
bfa_trc(port->fcs, ls_rjt->reason_code);
|
|
bfa_trc(port->fcs, ls_rjt->reason_code_expl);
|
|
|
|
bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR);
|
|
break;
|
|
|
|
default:
|
|
bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send a LS Accept
|
|
*/
|
|
static void
|
|
bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port,
|
|
struct fchs_s *rx_fchs)
|
|
{
|
|
struct fchs_s fchs;
|
|
struct bfa_fcxp_s *fcxp;
|
|
struct bfa_rport_s *bfa_rport = NULL;
|
|
int len;
|
|
|
|
bfa_trc(port->fcs, rx_fchs->s_id);
|
|
|
|
fcxp = bfa_fcs_fcxp_alloc(port->fcs);
|
|
if (!fcxp)
|
|
return;
|
|
|
|
len = fc_ls_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id,
|
|
bfa_fcs_port_get_fcid(port), rx_fchs->ox_id);
|
|
|
|
bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag,
|
|
BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL,
|
|
FC_MAX_PDUSZ, 0);
|
|
}
|
|
|
|
/**
|
|
* This routine will be called by bfa_timer on timer timeouts.
|
|
*
|
|
* param[in] vport - pointer to bfa_fcs_port_t.
|
|
* param[out] vport_status - pointer to return vport status in
|
|
*
|
|
* return
|
|
* void
|
|
*
|
|
* Special Considerations:
|
|
*
|
|
* note
|
|
*/
|
|
static void
|
|
bfa_fcs_port_scn_timeout(void *arg)
|
|
{
|
|
struct bfa_fcs_port_scn_s *scn = (struct bfa_fcs_port_scn_s *)arg;
|
|
|
|
bfa_sm_send_event(scn, SCNSM_EVENT_TIMEOUT);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* fcs_scn_public FCS state change notification public interfaces
|
|
*/
|
|
|
|
/*
|
|
* Functions called by port/fab
|
|
*/
|
|
void
|
|
bfa_fcs_port_scn_init(struct bfa_fcs_port_s *port)
|
|
{
|
|
struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port);
|
|
|
|
scn->port = port;
|
|
bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline);
|
|
}
|
|
|
|
void
|
|
bfa_fcs_port_scn_offline(struct bfa_fcs_port_s *port)
|
|
{
|
|
struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port);
|
|
|
|
scn->port = port;
|
|
bfa_sm_send_event(scn, SCNSM_EVENT_PORT_OFFLINE);
|
|
}
|
|
|
|
void
|
|
bfa_fcs_port_scn_online(struct bfa_fcs_port_s *port)
|
|
{
|
|
struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port);
|
|
|
|
scn->port = port;
|
|
bfa_sm_send_event(scn, SCNSM_EVENT_PORT_ONLINE);
|
|
}
|
|
|
|
static void
|
|
bfa_fcs_port_scn_portid_rscn(struct bfa_fcs_port_s *port, u32 rpid)
|
|
{
|
|
struct bfa_fcs_rport_s *rport;
|
|
|
|
bfa_trc(port->fcs, rpid);
|
|
|
|
/**
|
|
* If this is an unknown device, then it just came online.
|
|
* Otherwise let rport handle the RSCN event.
|
|
*/
|
|
rport = bfa_fcs_port_get_rport_by_pid(port, rpid);
|
|
if (rport == NULL) {
|
|
/*
|
|
* If min cfg mode is enabled, we donot need to
|
|
* discover any new rports.
|
|
*/
|
|
if (!__fcs_min_cfg(port->fcs))
|
|
rport = bfa_fcs_rport_create(port, rpid);
|
|
} else {
|
|
bfa_fcs_rport_scn(rport);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* rscn format based PID comparison
|
|
*/
|
|
#define __fc_pid_match(__c0, __c1, __fmt) \
|
|
(((__fmt) == FC_RSCN_FORMAT_FABRIC) || \
|
|
(((__fmt) == FC_RSCN_FORMAT_DOMAIN) && \
|
|
((__c0)[0] == (__c1)[0])) || \
|
|
(((__fmt) == FC_RSCN_FORMAT_AREA) && \
|
|
((__c0)[0] == (__c1)[0]) && \
|
|
((__c0)[1] == (__c1)[1])))
|
|
|
|
static void
|
|
bfa_fcs_port_scn_multiport_rscn(struct bfa_fcs_port_s *port,
|
|
enum fc_rscn_format format, u32 rscn_pid)
|
|
{
|
|
struct bfa_fcs_rport_s *rport;
|
|
struct list_head *qe, *qe_next;
|
|
u8 *c0, *c1;
|
|
|
|
bfa_trc(port->fcs, format);
|
|
bfa_trc(port->fcs, rscn_pid);
|
|
|
|
c0 = (u8 *) &rscn_pid;
|
|
|
|
list_for_each_safe(qe, qe_next, &port->rport_q) {
|
|
rport = (struct bfa_fcs_rport_s *)qe;
|
|
c1 = (u8 *) &rport->pid;
|
|
if (__fc_pid_match(c0, c1, format))
|
|
bfa_fcs_rport_scn(rport);
|
|
}
|
|
}
|
|
|
|
void
|
|
bfa_fcs_port_scn_process_rscn(struct bfa_fcs_port_s *port, struct fchs_s *fchs,
|
|
u32 len)
|
|
{
|
|
struct fc_rscn_pl_s *rscn = (struct fc_rscn_pl_s *) (fchs + 1);
|
|
int num_entries;
|
|
u32 rscn_pid;
|
|
bfa_boolean_t nsquery = BFA_FALSE;
|
|
int i = 0;
|
|
|
|
num_entries =
|
|
(bfa_os_ntohs(rscn->payldlen) -
|
|
sizeof(u32)) / sizeof(rscn->event[0]);
|
|
|
|
bfa_trc(port->fcs, num_entries);
|
|
|
|
port->stats.num_rscn++;
|
|
|
|
bfa_fcs_port_scn_send_ls_acc(port, fchs);
|
|
|
|
for (i = 0; i < num_entries; i++) {
|
|
rscn_pid = rscn->event[i].portid;
|
|
|
|
bfa_trc(port->fcs, rscn->event[i].format);
|
|
bfa_trc(port->fcs, rscn_pid);
|
|
|
|
switch (rscn->event[i].format) {
|
|
case FC_RSCN_FORMAT_PORTID:
|
|
if (rscn->event[i].qualifier == FC_QOS_RSCN_EVENT) {
|
|
/*
|
|
* Ignore this event. f/w would have processed
|
|
* it
|
|
*/
|
|
bfa_trc(port->fcs, rscn_pid);
|
|
} else {
|
|
port->stats.num_portid_rscn++;
|
|
bfa_fcs_port_scn_portid_rscn(port, rscn_pid);
|
|
}
|
|
break;
|
|
|
|
case FC_RSCN_FORMAT_FABRIC:
|
|
if (rscn->event[i].qualifier ==
|
|
FC_FABRIC_NAME_RSCN_EVENT) {
|
|
bfa_fcs_port_ms_fabric_rscn(port);
|
|
break;
|
|
}
|
|
/*
|
|
* !!!!!!!!! Fall Through !!!!!!!!!!!!!
|
|
*/
|
|
|
|
case FC_RSCN_FORMAT_AREA:
|
|
case FC_RSCN_FORMAT_DOMAIN:
|
|
nsquery = BFA_TRUE;
|
|
bfa_fcs_port_scn_multiport_rscn(port,
|
|
rscn->event[i].format,
|
|
rscn_pid);
|
|
break;
|
|
|
|
default:
|
|
bfa_assert(0);
|
|
nsquery = BFA_TRUE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If any of area, domain or fabric RSCN is received, do a fresh discovery
|
|
* to find new devices.
|
|
*/
|
|
if (nsquery)
|
|
bfa_fcs_port_ns_query(port);
|
|
}
|
|
|
|
|