2012-11-15 21:11:18 +04:00
/*
* This file is part of the Chelsio FCoE driver for Linux .
*
* Copyright ( c ) 2008 - 2012 Chelsio Communications , Inc . All rights reserved .
*
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the
* OpenIB . org BSD license below :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include <linux/string.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_transport_fc.h>
# include <scsi/fc/fc_els.h>
# include <scsi/fc/fc_fs.h>
# include "csio_hw.h"
# include "csio_lnode.h"
# include "csio_rnode.h"
static int csio_rnode_init ( struct csio_rnode * , struct csio_lnode * ) ;
static void csio_rnode_exit ( struct csio_rnode * ) ;
/* Static machine forward declarations */
static void csio_rns_uninit ( struct csio_rnode * , enum csio_rn_ev ) ;
static void csio_rns_ready ( struct csio_rnode * , enum csio_rn_ev ) ;
static void csio_rns_offline ( struct csio_rnode * , enum csio_rn_ev ) ;
static void csio_rns_disappeared ( struct csio_rnode * , enum csio_rn_ev ) ;
/* RNF event mapping */
static enum csio_rn_ev fwevt_to_rnevt [ ] = {
CSIO_RNFE_NONE , /* None */
CSIO_RNFE_LOGGED_IN , /* PLOGI_ACC_RCVD */
CSIO_RNFE_NONE , /* PLOGI_RJT_RCVD */
CSIO_RNFE_PLOGI_RECV , /* PLOGI_RCVD */
CSIO_RNFE_LOGO_RECV , /* PLOGO_RCVD */
CSIO_RNFE_PRLI_DONE , /* PRLI_ACC_RCVD */
CSIO_RNFE_NONE , /* PRLI_RJT_RCVD */
CSIO_RNFE_PRLI_RECV , /* PRLI_RCVD */
CSIO_RNFE_PRLO_RECV , /* PRLO_RCVD */
CSIO_RNFE_NONE , /* NPORT_ID_CHGD */
CSIO_RNFE_LOGO_RECV , /* FLOGO_RCVD */
CSIO_RNFE_NONE , /* CLR_VIRT_LNK_RCVD */
CSIO_RNFE_LOGGED_IN , /* FLOGI_ACC_RCVD */
CSIO_RNFE_NONE , /* FLOGI_RJT_RCVD */
CSIO_RNFE_LOGGED_IN , /* FDISC_ACC_RCVD */
CSIO_RNFE_NONE , /* FDISC_RJT_RCVD */
CSIO_RNFE_NONE , /* FLOGI_TMO_MAX_RETRY */
CSIO_RNFE_NONE , /* IMPL_LOGO_ADISC_ACC */
CSIO_RNFE_NONE , /* IMPL_LOGO_ADISC_RJT */
CSIO_RNFE_NONE , /* IMPL_LOGO_ADISC_CNFLT */
CSIO_RNFE_NONE , /* PRLI_TMO */
CSIO_RNFE_NONE , /* ADISC_TMO */
CSIO_RNFE_NAME_MISSING , /* RSCN_DEV_LOST */
CSIO_RNFE_NONE , /* SCR_ACC_RCVD */
CSIO_RNFE_NONE , /* ADISC_RJT_RCVD */
CSIO_RNFE_NONE , /* LOGO_SNT */
CSIO_RNFE_LOGO_RECV , /* PROTO_ERR_IMPL_LOGO */
} ;
# define CSIO_FWE_TO_RNFE(_evt) ((_evt > PROTO_ERR_IMPL_LOGO) ? \
CSIO_RNFE_NONE : \
fwevt_to_rnevt [ _evt ] )
int
csio_is_rnode_ready ( struct csio_rnode * rn )
{
return csio_match_state ( rn , csio_rns_ready ) ;
}
static int
csio_is_rnode_uninit ( struct csio_rnode * rn )
{
return csio_match_state ( rn , csio_rns_uninit ) ;
}
static int
csio_is_rnode_wka ( uint8_t rport_type )
{
if ( ( rport_type = = FLOGI_VFPORT ) | |
( rport_type = = FDISC_VFPORT ) | |
( rport_type = = NS_VNPORT ) | |
( rport_type = = FDMI_VNPORT ) )
return 1 ;
return 0 ;
}
/*
* csio_rn_lookup - Finds the rnode with the given flowid
* @ ln - lnode
* @ flowid - flowid .
*
* Does the rnode lookup on the given lnode and flowid . If no matching entry
* found , NULL is returned .
*/
static struct csio_rnode *
csio_rn_lookup ( struct csio_lnode * ln , uint32_t flowid )
{
struct csio_rnode * rnhead = ( struct csio_rnode * ) & ln - > rnhead ;
struct list_head * tmp ;
struct csio_rnode * rn ;
list_for_each ( tmp , & rnhead - > sm . sm_list ) {
rn = ( struct csio_rnode * ) tmp ;
if ( rn - > flowid = = flowid )
return rn ;
}
return NULL ;
}
/*
* csio_rn_lookup_wwpn - Finds the rnode with the given wwpn
* @ ln : lnode
* @ wwpn : wwpn
*
* Does the rnode lookup on the given lnode and wwpn . If no matching entry
* found , NULL is returned .
*/
static struct csio_rnode *
csio_rn_lookup_wwpn ( struct csio_lnode * ln , uint8_t * wwpn )
{
struct csio_rnode * rnhead = ( struct csio_rnode * ) & ln - > rnhead ;
struct list_head * tmp ;
struct csio_rnode * rn ;
list_for_each ( tmp , & rnhead - > sm . sm_list ) {
rn = ( struct csio_rnode * ) tmp ;
if ( ! memcmp ( csio_rn_wwpn ( rn ) , wwpn , 8 ) )
return rn ;
}
return NULL ;
}
/**
* csio_rnode_lookup_portid - Finds the rnode with the given portid
* @ ln : lnode
* @ portid : port id
*
* Lookup the rnode list for a given portid . If no matching entry
* found , NULL is returned .
*/
struct csio_rnode *
csio_rnode_lookup_portid ( struct csio_lnode * ln , uint32_t portid )
{
struct csio_rnode * rnhead = ( struct csio_rnode * ) & ln - > rnhead ;
struct list_head * tmp ;
struct csio_rnode * rn ;
list_for_each ( tmp , & rnhead - > sm . sm_list ) {
rn = ( struct csio_rnode * ) tmp ;
if ( rn - > nport_id = = portid )
return rn ;
}
return NULL ;
}
static int
csio_rn_dup_flowid ( struct csio_lnode * ln , uint32_t rdev_flowid ,
uint32_t * vnp_flowid )
{
struct csio_rnode * rnhead ;
struct list_head * tmp , * tmp1 ;
struct csio_rnode * rn ;
struct csio_lnode * ln_tmp ;
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
list_for_each ( tmp1 , & hw - > sln_head ) {
ln_tmp = ( struct csio_lnode * ) tmp1 ;
if ( ln_tmp = = ln )
continue ;
rnhead = ( struct csio_rnode * ) & ln_tmp - > rnhead ;
list_for_each ( tmp , & rnhead - > sm . sm_list ) {
rn = ( struct csio_rnode * ) tmp ;
if ( csio_is_rnode_ready ( rn ) ) {
if ( rn - > flowid = = rdev_flowid ) {
* vnp_flowid = csio_ln_flowid ( ln_tmp ) ;
return 1 ;
}
}
}
}
return 0 ;
}
static struct csio_rnode *
csio_alloc_rnode ( struct csio_lnode * ln )
{
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
struct csio_rnode * rn = mempool_alloc ( hw - > rnode_mempool , GFP_ATOMIC ) ;
if ( ! rn )
goto err ;
memset ( rn , 0 , sizeof ( struct csio_rnode ) ) ;
if ( csio_rnode_init ( rn , ln ) )
goto err_free ;
CSIO_INC_STATS ( ln , n_rnode_alloc ) ;
return rn ;
err_free :
mempool_free ( rn , hw - > rnode_mempool ) ;
err :
CSIO_INC_STATS ( ln , n_rnode_nomem ) ;
return NULL ;
}
static void
csio_free_rnode ( struct csio_rnode * rn )
{
struct csio_hw * hw = csio_lnode_to_hw ( csio_rnode_to_lnode ( rn ) ) ;
csio_rnode_exit ( rn ) ;
CSIO_INC_STATS ( rn - > lnp , n_rnode_free ) ;
mempool_free ( rn , hw - > rnode_mempool ) ;
}
/*
* csio_get_rnode - Gets rnode with the given flowid
* @ ln - lnode
* @ flowid - flow id .
*
* Does the rnode lookup on the given lnode and flowid . If no matching
* rnode found , then new rnode with given npid is allocated and returned .
*/
static struct csio_rnode *
csio_get_rnode ( struct csio_lnode * ln , uint32_t flowid )
{
struct csio_rnode * rn ;
rn = csio_rn_lookup ( ln , flowid ) ;
if ( ! rn ) {
rn = csio_alloc_rnode ( ln ) ;
if ( ! rn )
return NULL ;
rn - > flowid = flowid ;
}
return rn ;
}
/*
* csio_put_rnode - Frees the given rnode
* @ ln - lnode
* @ flowid - flow id .
*
* Does the rnode lookup on the given lnode and flowid . If no matching
* rnode found , then new rnode with given npid is allocated and returned .
*/
void
csio_put_rnode ( struct csio_lnode * ln , struct csio_rnode * rn )
{
CSIO_DB_ASSERT ( csio_is_rnode_uninit ( rn ) ! = 0 ) ;
csio_free_rnode ( rn ) ;
}
/*
* csio_confirm_rnode - confirms rnode based on wwpn .
* @ ln : lnode
* @ rdev_flowid : remote device flowid
* @ rdevp : remote device params
* This routines searches other rnode in list having same wwpn of new rnode .
* If there is a match , then matched rnode is returned and otherwise new rnode
* is returned .
* returns rnode .
*/
struct csio_rnode *
csio_confirm_rnode ( struct csio_lnode * ln , uint32_t rdev_flowid ,
struct fcoe_rdev_entry * rdevp )
{
uint8_t rport_type ;
struct csio_rnode * rn , * match_rn ;
2013-03-14 09:09:07 +04:00
uint32_t vnp_flowid = 0 ;
2012-11-20 16:45:40 +04:00
__be32 * port_id ;
2012-11-15 21:11:18 +04:00
2012-11-20 16:45:40 +04:00
port_id = ( __be32 * ) & rdevp - > r_id [ 0 ] ;
2012-11-15 21:11:18 +04:00
rport_type =
FW_RDEV_WR_RPORT_TYPE_GET ( rdevp - > rd_xfer_rdy_to_rport_type ) ;
/* Drop rdev event for cntrl port */
if ( rport_type = = FAB_CTLR_VNPORT ) {
csio_ln_dbg ( ln ,
" Unhandled rport_type:%d recv in rdev evt "
" ssni:x%x \n " , rport_type , rdev_flowid ) ;
return NULL ;
}
/* Lookup on flowid */
rn = csio_rn_lookup ( ln , rdev_flowid ) ;
if ( ! rn ) {
/* Drop events with duplicate flowid */
if ( csio_rn_dup_flowid ( ln , rdev_flowid , & vnp_flowid ) ) {
csio_ln_warn ( ln ,
" ssni:%x already active on vnpi:%x " ,
rdev_flowid , vnp_flowid ) ;
return NULL ;
}
/* Lookup on wwpn for NPORTs */
rn = csio_rn_lookup_wwpn ( ln , rdevp - > wwpn ) ;
if ( ! rn )
goto alloc_rnode ;
} else {
/* Lookup well-known ports with nport id */
if ( csio_is_rnode_wka ( rport_type ) ) {
match_rn = csio_rnode_lookup_portid ( ln ,
( ( ntohl ( * port_id ) > > 8 ) & CSIO_DID_MASK ) ) ;
if ( match_rn = = NULL ) {
csio_rn_flowid ( rn ) = CSIO_INVALID_IDX ;
goto alloc_rnode ;
}
/*
* Now compare the wwpn to confirm that
* same port relogged in . If so update the matched rn .
* Else , go ahead and alloc a new rnode .
*/
if ( ! memcmp ( csio_rn_wwpn ( match_rn ) , rdevp - > wwpn , 8 ) ) {
2013-03-14 09:09:07 +04:00
if ( rn = = match_rn )
goto found_rnode ;
csio_ln_dbg ( ln ,
" nport_id:x%x and wwpn:%llx "
" match for ssni:x%x \n " ,
rn - > nport_id ,
wwn_to_u64 ( rdevp - > wwpn ) ,
rdev_flowid ) ;
2012-11-15 21:11:18 +04:00
if ( csio_is_rnode_ready ( rn ) ) {
csio_ln_warn ( ln ,
" rnode is already "
" active ssni:x%x \n " ,
rdev_flowid ) ;
CSIO_ASSERT ( 0 ) ;
}
csio_rn_flowid ( rn ) = CSIO_INVALID_IDX ;
rn = match_rn ;
/* Update rn */
goto found_rnode ;
}
csio_rn_flowid ( rn ) = CSIO_INVALID_IDX ;
goto alloc_rnode ;
}
/* wwpn match */
if ( ! memcmp ( csio_rn_wwpn ( rn ) , rdevp - > wwpn , 8 ) )
goto found_rnode ;
/* Search for rnode that have same wwpn */
match_rn = csio_rn_lookup_wwpn ( ln , rdevp - > wwpn ) ;
if ( match_rn ! = NULL ) {
csio_ln_dbg ( ln ,
" ssni:x%x changed for rport name(wwpn):%llx "
" did:x%x \n " , rdev_flowid ,
wwn_to_u64 ( rdevp - > wwpn ) ,
match_rn - > nport_id ) ;
csio_rn_flowid ( rn ) = CSIO_INVALID_IDX ;
rn = match_rn ;
} else {
csio_ln_dbg ( ln ,
" rnode wwpn mismatch found ssni:x%x "
" name(wwpn):%llx \n " ,
rdev_flowid ,
wwn_to_u64 ( csio_rn_wwpn ( rn ) ) ) ;
if ( csio_is_rnode_ready ( rn ) ) {
csio_ln_warn ( ln ,
" rnode is already active "
" wwpn:%llx ssni:x%x \n " ,
wwn_to_u64 ( csio_rn_wwpn ( rn ) ) ,
rdev_flowid ) ;
CSIO_ASSERT ( 0 ) ;
}
csio_rn_flowid ( rn ) = CSIO_INVALID_IDX ;
goto alloc_rnode ;
}
}
found_rnode :
csio_ln_dbg ( ln , " found rnode:%p ssni:x%x name(wwpn):%llx \n " ,
rn , rdev_flowid , wwn_to_u64 ( rdevp - > wwpn ) ) ;
/* Update flowid */
csio_rn_flowid ( rn ) = rdev_flowid ;
/* update rdev entry */
rn - > rdev_entry = rdevp ;
CSIO_INC_STATS ( ln , n_rnode_match ) ;
return rn ;
alloc_rnode :
rn = csio_get_rnode ( ln , rdev_flowid ) ;
if ( ! rn )
return NULL ;
csio_ln_dbg ( ln , " alloc rnode:%p ssni:x%x name(wwpn):%llx \n " ,
rn , rdev_flowid , wwn_to_u64 ( rdevp - > wwpn ) ) ;
/* update rdev entry */
rn - > rdev_entry = rdevp ;
return rn ;
}
/*
* csio_rn_verify_rparams - verify rparams .
* @ ln : lnode
* @ rn : rnode
* @ rdevp : remote device params
* returns success if rparams are verified .
*/
static int
csio_rn_verify_rparams ( struct csio_lnode * ln , struct csio_rnode * rn ,
struct fcoe_rdev_entry * rdevp )
{
uint8_t null [ 8 ] ;
uint8_t rport_type ;
uint8_t fc_class ;
2012-11-20 16:45:40 +04:00
__be32 * did ;
2012-11-15 21:11:18 +04:00
2012-11-20 16:45:40 +04:00
did = ( __be32 * ) & rdevp - > r_id [ 0 ] ;
2012-11-15 21:11:18 +04:00
rport_type =
FW_RDEV_WR_RPORT_TYPE_GET ( rdevp - > rd_xfer_rdy_to_rport_type ) ;
switch ( rport_type ) {
case FLOGI_VFPORT :
rn - > role = CSIO_RNFR_FABRIC ;
if ( ( ( ntohl ( * did ) > > 8 ) & CSIO_DID_MASK ) ! = FC_FID_FLOGI ) {
csio_ln_err ( ln , " ssni:x%x invalid fabric portid \n " ,
csio_rn_flowid ( rn ) ) ;
return - EINVAL ;
}
/* NPIV support */
if ( FW_RDEV_WR_NPIV_GET ( rdevp - > vft_to_qos ) )
ln - > flags | = CSIO_LNF_NPIVSUPP ;
break ;
case NS_VNPORT :
rn - > role = CSIO_RNFR_NS ;
if ( ( ( ntohl ( * did ) > > 8 ) & CSIO_DID_MASK ) ! = FC_FID_DIR_SERV ) {
csio_ln_err ( ln , " ssni:x%x invalid fabric portid \n " ,
csio_rn_flowid ( rn ) ) ;
return - EINVAL ;
}
break ;
case REG_FC4_VNPORT :
case REG_VNPORT :
rn - > role = CSIO_RNFR_NPORT ;
if ( rdevp - > event_cause = = PRLI_ACC_RCVD | |
rdevp - > event_cause = = PRLI_RCVD ) {
if ( FW_RDEV_WR_TASK_RETRY_ID_GET (
rdevp - > enh_disc_to_tgt ) )
rn - > fcp_flags | = FCP_SPPF_OVLY_ALLOW ;
if ( FW_RDEV_WR_RETRY_GET ( rdevp - > enh_disc_to_tgt ) )
rn - > fcp_flags | = FCP_SPPF_RETRY ;
if ( FW_RDEV_WR_CONF_CMPL_GET ( rdevp - > enh_disc_to_tgt ) )
rn - > fcp_flags | = FCP_SPPF_CONF_COMPL ;
if ( FW_RDEV_WR_TGT_GET ( rdevp - > enh_disc_to_tgt ) )
rn - > role | = CSIO_RNFR_TARGET ;
if ( FW_RDEV_WR_INI_GET ( rdevp - > enh_disc_to_tgt ) )
rn - > role | = CSIO_RNFR_INITIATOR ;
}
break ;
case FDMI_VNPORT :
case FAB_CTLR_VNPORT :
rn - > role = 0 ;
break ;
default :
csio_ln_err ( ln , " ssni:x%x invalid rport type recv x%x \n " ,
csio_rn_flowid ( rn ) , rport_type ) ;
return - EINVAL ;
}
/* validate wwpn/wwnn for Name server/remote port */
if ( rport_type = = REG_VNPORT | | rport_type = = NS_VNPORT ) {
memset ( null , 0 , 8 ) ;
if ( ! memcmp ( rdevp - > wwnn , null , 8 ) ) {
csio_ln_err ( ln ,
" ssni:x%x invalid wwnn received from "
" rport did:x%x \n " ,
csio_rn_flowid ( rn ) ,
( ntohl ( * did ) & CSIO_DID_MASK ) ) ;
return - EINVAL ;
}
if ( ! memcmp ( rdevp - > wwpn , null , 8 ) ) {
csio_ln_err ( ln ,
" ssni:x%x invalid wwpn received from "
" rport did:x%x \n " ,
csio_rn_flowid ( rn ) ,
( ntohl ( * did ) & CSIO_DID_MASK ) ) ;
return - EINVAL ;
}
}
/* Copy wwnn, wwpn and nport id */
rn - > nport_id = ( ntohl ( * did ) > > 8 ) & CSIO_DID_MASK ;
memcpy ( csio_rn_wwnn ( rn ) , rdevp - > wwnn , 8 ) ;
memcpy ( csio_rn_wwpn ( rn ) , rdevp - > wwpn , 8 ) ;
2012-11-20 16:45:40 +04:00
rn - > rn_sparm . csp . sp_bb_data = rdevp - > rcv_fr_sz ;
2012-11-15 21:11:18 +04:00
fc_class = FW_RDEV_WR_CLASS_GET ( rdevp - > vft_to_qos ) ;
rn - > rn_sparm . clsp [ fc_class - 1 ] . cp_class = htons ( FC_CPC_VALID ) ;
2012-11-20 16:45:40 +04:00
2012-11-15 21:11:18 +04:00
return 0 ;
}
static void
__csio_reg_rnode ( struct csio_rnode * rn )
{
struct csio_lnode * ln = csio_rnode_to_lnode ( rn ) ;
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
spin_unlock_irq ( & hw - > lock ) ;
csio_reg_rnode ( rn ) ;
spin_lock_irq ( & hw - > lock ) ;
if ( rn - > role & CSIO_RNFR_TARGET )
ln - > n_scsi_tgts + + ;
if ( rn - > nport_id = = FC_FID_MGMT_SERV )
csio_ln_fdmi_start ( ln , ( void * ) rn ) ;
}
static void
__csio_unreg_rnode ( struct csio_rnode * rn )
{
struct csio_lnode * ln = csio_rnode_to_lnode ( rn ) ;
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
LIST_HEAD ( tmp_q ) ;
int cmpl = 0 ;
if ( ! list_empty ( & rn - > host_cmpl_q ) ) {
csio_dbg ( hw , " Returning completion queue I/Os \n " ) ;
list_splice_tail_init ( & rn - > host_cmpl_q , & tmp_q ) ;
cmpl = 1 ;
}
if ( rn - > role & CSIO_RNFR_TARGET ) {
ln - > n_scsi_tgts - - ;
ln - > last_scan_ntgts - - ;
}
spin_unlock_irq ( & hw - > lock ) ;
csio_unreg_rnode ( rn ) ;
spin_lock_irq ( & hw - > lock ) ;
/* Cleanup I/Os that were waiting for rnode to unregister */
if ( cmpl )
csio_scsi_cleanup_io_q ( csio_hw_to_scsim ( hw ) , & tmp_q ) ;
}
/*****************************************************************************/
/* START: Rnode SM */
/*****************************************************************************/
/*
* csio_rns_uninit -
* @ rn - rnode
* @ evt - SM event .
*
*/
static void
csio_rns_uninit ( struct csio_rnode * rn , enum csio_rn_ev evt )
{
struct csio_lnode * ln = csio_rnode_to_lnode ( rn ) ;
int ret = 0 ;
CSIO_INC_STATS ( rn , n_evt_sm [ evt ] ) ;
switch ( evt ) {
case CSIO_RNFE_LOGGED_IN :
case CSIO_RNFE_PLOGI_RECV :
ret = csio_rn_verify_rparams ( ln , rn , rn - > rdev_entry ) ;
if ( ! ret ) {
csio_set_state ( & rn - > sm , csio_rns_ready ) ;
__csio_reg_rnode ( rn ) ;
} else {
CSIO_INC_STATS ( rn , n_err_inval ) ;
}
break ;
case CSIO_RNFE_LOGO_RECV :
csio_ln_dbg ( ln ,
" ssni:x%x Ignoring event %d recv "
" in rn state[uninit] \n " , csio_rn_flowid ( rn ) , evt ) ;
CSIO_INC_STATS ( rn , n_evt_drop ) ;
break ;
default :
csio_ln_dbg ( ln ,
" ssni:x%x unexp event %d recv "
" in rn state[uninit] \n " , csio_rn_flowid ( rn ) , evt ) ;
CSIO_INC_STATS ( rn , n_evt_unexp ) ;
break ;
}
}
/*
* csio_rns_ready -
* @ rn - rnode
* @ evt - SM event .
*
*/
static void
csio_rns_ready ( struct csio_rnode * rn , enum csio_rn_ev evt )
{
struct csio_lnode * ln = csio_rnode_to_lnode ( rn ) ;
int ret = 0 ;
CSIO_INC_STATS ( rn , n_evt_sm [ evt ] ) ;
switch ( evt ) {
case CSIO_RNFE_LOGGED_IN :
case CSIO_RNFE_PLOGI_RECV :
csio_ln_dbg ( ln ,
" ssni:x%x Ignoring event %d recv from did:x%x "
" in rn state[ready] \n " , csio_rn_flowid ( rn ) , evt ,
rn - > nport_id ) ;
CSIO_INC_STATS ( rn , n_evt_drop ) ;
break ;
case CSIO_RNFE_PRLI_DONE :
case CSIO_RNFE_PRLI_RECV :
ret = csio_rn_verify_rparams ( ln , rn , rn - > rdev_entry ) ;
if ( ! ret )
__csio_reg_rnode ( rn ) ;
else
CSIO_INC_STATS ( rn , n_err_inval ) ;
break ;
case CSIO_RNFE_DOWN :
csio_set_state ( & rn - > sm , csio_rns_offline ) ;
__csio_unreg_rnode ( rn ) ;
/* FW expected to internally aborted outstanding SCSI WRs
* and return all SCSI WRs to host with status " ABORTED " .
*/
break ;
case CSIO_RNFE_LOGO_RECV :
csio_set_state ( & rn - > sm , csio_rns_offline ) ;
__csio_unreg_rnode ( rn ) ;
/* FW expected to internally aborted outstanding SCSI WRs
* and return all SCSI WRs to host with status " ABORTED " .
*/
break ;
case CSIO_RNFE_CLOSE :
/*
* Each rnode receives CLOSE event when driver is removed or
* device is reset
* Note : All outstanding IOs on remote port need to returned
* to uppper layer with appropriate error before sending
* CLOSE event
*/
csio_set_state ( & rn - > sm , csio_rns_uninit ) ;
__csio_unreg_rnode ( rn ) ;
break ;
case CSIO_RNFE_NAME_MISSING :
csio_set_state ( & rn - > sm , csio_rns_disappeared ) ;
__csio_unreg_rnode ( rn ) ;
/*
* FW expected to internally aborted outstanding SCSI WRs
* and return all SCSI WRs to host with status " ABORTED " .
*/
break ;
default :
csio_ln_dbg ( ln ,
" ssni:x%x unexp event %d recv from did:x%x "
" in rn state[uninit] \n " , csio_rn_flowid ( rn ) , evt ,
rn - > nport_id ) ;
CSIO_INC_STATS ( rn , n_evt_unexp ) ;
break ;
}
}
/*
* csio_rns_offline -
* @ rn - rnode
* @ evt - SM event .
*
*/
static void
csio_rns_offline ( struct csio_rnode * rn , enum csio_rn_ev evt )
{
struct csio_lnode * ln = csio_rnode_to_lnode ( rn ) ;
int ret = 0 ;
CSIO_INC_STATS ( rn , n_evt_sm [ evt ] ) ;
switch ( evt ) {
case CSIO_RNFE_LOGGED_IN :
case CSIO_RNFE_PLOGI_RECV :
ret = csio_rn_verify_rparams ( ln , rn , rn - > rdev_entry ) ;
if ( ! ret ) {
csio_set_state ( & rn - > sm , csio_rns_ready ) ;
__csio_reg_rnode ( rn ) ;
} else {
CSIO_INC_STATS ( rn , n_err_inval ) ;
csio_post_event ( & rn - > sm , CSIO_RNFE_CLOSE ) ;
}
break ;
case CSIO_RNFE_DOWN :
csio_ln_dbg ( ln ,
" ssni:x%x Ignoring event %d recv from did:x%x "
" in rn state[offline] \n " , csio_rn_flowid ( rn ) , evt ,
rn - > nport_id ) ;
CSIO_INC_STATS ( rn , n_evt_drop ) ;
break ;
case CSIO_RNFE_CLOSE :
/* Each rnode receives CLOSE event when driver is removed or
* device is reset
* Note : All outstanding IOs on remote port need to returned
* to uppper layer with appropriate error before sending
* CLOSE event
*/
csio_set_state ( & rn - > sm , csio_rns_uninit ) ;
break ;
case CSIO_RNFE_NAME_MISSING :
csio_set_state ( & rn - > sm , csio_rns_disappeared ) ;
break ;
default :
csio_ln_dbg ( ln ,
" ssni:x%x unexp event %d recv from did:x%x "
" in rn state[offline] \n " , csio_rn_flowid ( rn ) , evt ,
rn - > nport_id ) ;
CSIO_INC_STATS ( rn , n_evt_unexp ) ;
break ;
}
}
/*
* csio_rns_disappeared -
* @ rn - rnode
* @ evt - SM event .
*
*/
static void
csio_rns_disappeared ( struct csio_rnode * rn , enum csio_rn_ev evt )
{
struct csio_lnode * ln = csio_rnode_to_lnode ( rn ) ;
int ret = 0 ;
CSIO_INC_STATS ( rn , n_evt_sm [ evt ] ) ;
switch ( evt ) {
case CSIO_RNFE_LOGGED_IN :
case CSIO_RNFE_PLOGI_RECV :
ret = csio_rn_verify_rparams ( ln , rn , rn - > rdev_entry ) ;
if ( ! ret ) {
csio_set_state ( & rn - > sm , csio_rns_ready ) ;
__csio_reg_rnode ( rn ) ;
} else {
CSIO_INC_STATS ( rn , n_err_inval ) ;
csio_post_event ( & rn - > sm , CSIO_RNFE_CLOSE ) ;
}
break ;
case CSIO_RNFE_CLOSE :
/* Each rnode receives CLOSE event when driver is removed or
* device is reset .
* Note : All outstanding IOs on remote port need to returned
* to uppper layer with appropriate error before sending
* CLOSE event
*/
csio_set_state ( & rn - > sm , csio_rns_uninit ) ;
break ;
case CSIO_RNFE_DOWN :
case CSIO_RNFE_NAME_MISSING :
csio_ln_dbg ( ln ,
" ssni:x%x Ignoring event %d recv from did x%x "
" in rn state[disappeared] \n " , csio_rn_flowid ( rn ) ,
evt , rn - > nport_id ) ;
break ;
default :
csio_ln_dbg ( ln ,
" ssni:x%x unexp event %d recv from did x%x "
" in rn state[disappeared] \n " , csio_rn_flowid ( rn ) ,
evt , rn - > nport_id ) ;
CSIO_INC_STATS ( rn , n_evt_unexp ) ;
break ;
}
}
/*****************************************************************************/
/* END: Rnode SM */
/*****************************************************************************/
/*
* csio_rnode_devloss_handler - Device loss event handler
* @ rn : rnode
*
* Post event to close rnode SM and free rnode .
*/
void
csio_rnode_devloss_handler ( struct csio_rnode * rn )
{
struct csio_lnode * ln = csio_rnode_to_lnode ( rn ) ;
/* ignore if same rnode came back as online */
if ( csio_is_rnode_ready ( rn ) )
return ;
csio_post_event ( & rn - > sm , CSIO_RNFE_CLOSE ) ;
/* Free rn if in uninit state */
if ( csio_is_rnode_uninit ( rn ) )
csio_put_rnode ( ln , rn ) ;
}
/**
* csio_rnode_fwevt_handler - Event handler for firmware rnode events .
* @ rn : rnode
*
*/
void
csio_rnode_fwevt_handler ( struct csio_rnode * rn , uint8_t fwevt )
{
struct csio_lnode * ln = csio_rnode_to_lnode ( rn ) ;
enum csio_rn_ev evt ;
evt = CSIO_FWE_TO_RNFE ( fwevt ) ;
if ( ! evt ) {
csio_ln_err ( ln , " ssni:x%x Unhandled FW Rdev event: %d \n " ,
csio_rn_flowid ( rn ) , fwevt ) ;
CSIO_INC_STATS ( rn , n_evt_unexp ) ;
return ;
}
CSIO_INC_STATS ( rn , n_evt_fw [ fwevt ] ) ;
/* Track previous & current events for debugging */
rn - > prev_evt = rn - > cur_evt ;
rn - > cur_evt = fwevt ;
/* Post event to rnode SM */
csio_post_event ( & rn - > sm , evt ) ;
/* Free rn if in uninit state */
if ( csio_is_rnode_uninit ( rn ) )
csio_put_rnode ( ln , rn ) ;
}
/*
* csio_rnode_init - Initialize rnode .
* @ rn : RNode
* @ ln : Associated lnode
*
* Caller is responsible for holding the lock . The lock is required
* to be held for inserting the rnode in ln - > rnhead list .
*/
static int
csio_rnode_init ( struct csio_rnode * rn , struct csio_lnode * ln )
{
csio_rnode_to_lnode ( rn ) = ln ;
csio_init_state ( & rn - > sm , csio_rns_uninit ) ;
INIT_LIST_HEAD ( & rn - > host_cmpl_q ) ;
csio_rn_flowid ( rn ) = CSIO_INVALID_IDX ;
/* Add rnode to list of lnodes->rnhead */
list_add_tail ( & rn - > sm . sm_list , & ln - > rnhead ) ;
return 0 ;
}
static void
csio_rnode_exit ( struct csio_rnode * rn )
{
list_del_init ( & rn - > sm . sm_list ) ;
CSIO_DB_ASSERT ( list_empty ( & rn - > host_cmpl_q ) ) ;
}