2012-11-15 22:41:18 +05:30
/*
* 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/kernel.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/utsname.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_transport_fc.h>
# include <asm/unaligned.h>
# include <scsi/fc/fc_els.h>
# include <scsi/fc/fc_fs.h>
# include <scsi/fc/fc_gs.h>
# include <scsi/fc/fc_ms.h>
# include "csio_hw.h"
# include "csio_mb.h"
# include "csio_lnode.h"
# include "csio_rnode.h"
int csio_fcoe_rnodes = 1024 ;
int csio_fdmi_enable = 1 ;
# define PORT_ID_PTR(_x) ((uint8_t *)(&_x) + 1)
/* Lnode SM declarations */
static void csio_lns_uninit ( struct csio_lnode * , enum csio_ln_ev ) ;
static void csio_lns_online ( struct csio_lnode * , enum csio_ln_ev ) ;
static void csio_lns_ready ( struct csio_lnode * , enum csio_ln_ev ) ;
static void csio_lns_offline ( struct csio_lnode * , enum csio_ln_ev ) ;
static int csio_ln_mgmt_submit_req ( struct csio_ioreq * ,
void ( * io_cbfn ) ( struct csio_hw * , struct csio_ioreq * ) ,
enum fcoe_cmn_type , struct csio_dma_buf * , uint32_t ) ;
/* LN event mapping */
static enum csio_ln_ev fwevt_to_lnevt [ ] = {
CSIO_LNE_NONE , /* None */
CSIO_LNE_NONE , /* PLOGI_ACC_RCVD */
CSIO_LNE_NONE , /* PLOGI_RJT_RCVD */
CSIO_LNE_NONE , /* PLOGI_RCVD */
CSIO_LNE_NONE , /* PLOGO_RCVD */
CSIO_LNE_NONE , /* PRLI_ACC_RCVD */
CSIO_LNE_NONE , /* PRLI_RJT_RCVD */
CSIO_LNE_NONE , /* PRLI_RCVD */
CSIO_LNE_NONE , /* PRLO_RCVD */
CSIO_LNE_NONE , /* NPORT_ID_CHGD */
CSIO_LNE_LOGO , /* FLOGO_RCVD */
CSIO_LNE_LOGO , /* CLR_VIRT_LNK_RCVD */
CSIO_LNE_FAB_INIT_DONE , /* FLOGI_ACC_RCVD */
CSIO_LNE_NONE , /* FLOGI_RJT_RCVD */
CSIO_LNE_FAB_INIT_DONE , /* FDISC_ACC_RCVD */
CSIO_LNE_NONE , /* FDISC_RJT_RCVD */
CSIO_LNE_NONE , /* FLOGI_TMO_MAX_RETRY */
CSIO_LNE_NONE , /* IMPL_LOGO_ADISC_ACC */
CSIO_LNE_NONE , /* IMPL_LOGO_ADISC_RJT */
CSIO_LNE_NONE , /* IMPL_LOGO_ADISC_CNFLT */
CSIO_LNE_NONE , /* PRLI_TMO */
CSIO_LNE_NONE , /* ADISC_TMO */
CSIO_LNE_NONE , /* RSCN_DEV_LOST */
CSIO_LNE_NONE , /* SCR_ACC_RCVD */
CSIO_LNE_NONE , /* ADISC_RJT_RCVD */
CSIO_LNE_NONE , /* LOGO_SNT */
CSIO_LNE_NONE , /* PROTO_ERR_IMPL_LOGO */
} ;
# define CSIO_FWE_TO_LNE(_evt) ((_evt > PROTO_ERR_IMPL_LOGO) ? \
CSIO_LNE_NONE : \
fwevt_to_lnevt [ _evt ] )
# define csio_ct_rsp(cp) (((struct fc_ct_hdr *)cp)->ct_cmd)
# define csio_ct_reason(cp) (((struct fc_ct_hdr *)cp)->ct_reason)
# define csio_ct_expl(cp) (((struct fc_ct_hdr *)cp)->ct_explan)
# define csio_ct_get_pld(cp) ((void *)(((uint8_t *)cp) + FC_CT_HDR_LEN))
/*
* csio_ln_match_by_portid - lookup lnode using given portid .
* @ hw : HW module
* @ portid : port - id .
*
* If found , returns lnode matching given portid otherwise returns NULL .
*/
static struct csio_lnode *
csio_ln_lookup_by_portid ( struct csio_hw * hw , uint8_t portid )
{
struct csio_lnode * ln = hw - > rln ;
struct list_head * tmp ;
/* Match siblings lnode with portid */
list_for_each ( tmp , & hw - > sln_head ) {
ln = ( struct csio_lnode * ) tmp ;
if ( ln - > portid = = portid )
return ln ;
}
return NULL ;
}
/*
* csio_ln_lookup_by_vnpi - Lookup lnode using given vnp id .
* @ hw - HW module
* @ vnpi - vnp index .
* Returns - If found , returns lnode matching given vnp id
* otherwise returns NULL .
*/
static struct csio_lnode *
csio_ln_lookup_by_vnpi ( struct csio_hw * hw , uint32_t vnp_id )
{
struct list_head * tmp1 , * tmp2 ;
struct csio_lnode * sln = NULL , * cln = NULL ;
if ( list_empty ( & hw - > sln_head ) ) {
CSIO_INC_STATS ( hw , n_lnlkup_miss ) ;
return NULL ;
}
/* Traverse sibling lnodes */
list_for_each ( tmp1 , & hw - > sln_head ) {
sln = ( struct csio_lnode * ) tmp1 ;
/* Match sibling lnode */
if ( sln - > vnp_flowid = = vnp_id )
return sln ;
if ( list_empty ( & sln - > cln_head ) )
continue ;
/* Traverse children lnodes */
list_for_each ( tmp2 , & sln - > cln_head ) {
cln = ( struct csio_lnode * ) tmp2 ;
if ( cln - > vnp_flowid = = vnp_id )
return cln ;
}
}
CSIO_INC_STATS ( hw , n_lnlkup_miss ) ;
return NULL ;
}
/**
* csio_lnode_lookup_by_wwpn - Lookup lnode using given wwpn .
* @ hw : HW module .
* @ wwpn : WWPN .
*
* If found , returns lnode matching given wwpn , returns NULL otherwise .
*/
struct csio_lnode *
csio_lnode_lookup_by_wwpn ( struct csio_hw * hw , uint8_t * wwpn )
{
struct list_head * tmp1 , * tmp2 ;
struct csio_lnode * sln = NULL , * cln = NULL ;
if ( list_empty ( & hw - > sln_head ) ) {
CSIO_INC_STATS ( hw , n_lnlkup_miss ) ;
return NULL ;
}
/* Traverse sibling lnodes */
list_for_each ( tmp1 , & hw - > sln_head ) {
sln = ( struct csio_lnode * ) tmp1 ;
/* Match sibling lnode */
if ( ! memcmp ( csio_ln_wwpn ( sln ) , wwpn , 8 ) )
return sln ;
if ( list_empty ( & sln - > cln_head ) )
continue ;
/* Traverse children lnodes */
list_for_each ( tmp2 , & sln - > cln_head ) {
cln = ( struct csio_lnode * ) tmp2 ;
if ( ! memcmp ( csio_ln_wwpn ( cln ) , wwpn , 8 ) )
return cln ;
}
}
return NULL ;
}
/* FDMI */
static void
csio_fill_ct_iu ( void * buf , uint8_t type , uint8_t sub_type , uint16_t op )
{
struct fc_ct_hdr * cmd = ( struct fc_ct_hdr * ) buf ;
cmd - > ct_rev = FC_CT_REV ;
cmd - > ct_fs_type = type ;
cmd - > ct_fs_subtype = sub_type ;
2012-11-20 18:15:40 +05:30
cmd - > ct_cmd = htons ( op ) ;
2012-11-15 22:41:18 +05:30
}
static int
csio_hostname ( uint8_t * buf , size_t buf_len )
{
if ( snprintf ( buf , buf_len , " %s " , init_utsname ( ) - > nodename ) > 0 )
return 0 ;
return - 1 ;
}
static int
csio_osname ( uint8_t * buf , size_t buf_len )
{
if ( snprintf ( buf , buf_len , " %s %s %s " ,
init_utsname ( ) - > sysname ,
init_utsname ( ) - > release ,
init_utsname ( ) - > version ) > 0 )
return 0 ;
return - 1 ;
}
static inline void
2017-05-09 15:34:44 -07:00
csio_append_attrib ( uint8_t * * ptr , uint16_t type , void * val , size_t val_len )
2012-11-15 22:41:18 +05:30
{
2017-05-09 15:34:44 -07:00
uint16_t len ;
2012-11-15 22:41:18 +05:30
struct fc_fdmi_attr_entry * ae = ( struct fc_fdmi_attr_entry * ) * ptr ;
2017-05-09 15:34:44 -07:00
if ( WARN_ON ( val_len > U16_MAX ) )
return ;
len = val_len ;
2012-11-15 22:41:18 +05:30
ae - > type = htons ( type ) ;
len + = 4 ; /* includes attribute type and length */
len = ( len + 3 ) & ~ 3 ; /* should be multiple of 4 bytes */
ae - > len = htons ( len ) ;
2017-05-09 15:34:44 -07:00
memcpy ( ae - > value , val , val_len ) ;
if ( len > val_len )
memset ( ae - > value + val_len , 0 , len - val_len ) ;
2012-11-15 22:41:18 +05:30
* ptr + = len ;
}
/*
* csio_ln_fdmi_done - FDMI registeration completion
* @ hw : HW context
* @ fdmi_req : fdmi request
*/
static void
csio_ln_fdmi_done ( struct csio_hw * hw , struct csio_ioreq * fdmi_req )
{
void * cmd ;
struct csio_lnode * ln = fdmi_req - > lnode ;
if ( fdmi_req - > wr_status ! = FW_SUCCESS ) {
csio_ln_dbg ( ln , " WR error:%x in processing fdmi rpa cmd \n " ,
fdmi_req - > wr_status ) ;
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
}
cmd = fdmi_req - > dma_buf . vaddr ;
if ( ntohs ( csio_ct_rsp ( cmd ) ) ! = FC_FS_ACC ) {
csio_ln_dbg ( ln , " fdmi rpa cmd rejected reason %x expl %x \n " ,
csio_ct_reason ( cmd ) , csio_ct_expl ( cmd ) ) ;
}
}
/*
* csio_ln_fdmi_rhba_cbfn - RHBA completion
* @ hw : HW context
* @ fdmi_req : fdmi request
*/
static void
csio_ln_fdmi_rhba_cbfn ( struct csio_hw * hw , struct csio_ioreq * fdmi_req )
{
void * cmd ;
uint8_t * pld ;
uint32_t len = 0 ;
2012-11-20 18:15:40 +05:30
__be32 val ;
__be16 mfs ;
uint32_t numattrs = 0 ;
2012-11-15 22:41:18 +05:30
struct csio_lnode * ln = fdmi_req - > lnode ;
struct fs_fdmi_attrs * attrib_blk ;
struct fc_fdmi_port_name * port_name ;
uint8_t buf [ 64 ] ;
uint8_t * fc4_type ;
if ( fdmi_req - > wr_status ! = FW_SUCCESS ) {
csio_ln_dbg ( ln , " WR error:%x in processing fdmi rhba cmd \n " ,
fdmi_req - > wr_status ) ;
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
}
cmd = fdmi_req - > dma_buf . vaddr ;
if ( ntohs ( csio_ct_rsp ( cmd ) ) ! = FC_FS_ACC ) {
csio_ln_dbg ( ln , " fdmi rhba cmd rejected reason %x expl %x \n " ,
csio_ct_reason ( cmd ) , csio_ct_expl ( cmd ) ) ;
}
if ( ! csio_is_rnode_ready ( fdmi_req - > rnode ) ) {
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
return ;
}
/* Prepare CT hdr for RPA cmd */
memset ( cmd , 0 , FC_CT_HDR_LEN ) ;
2012-11-20 18:15:40 +05:30
csio_fill_ct_iu ( cmd , FC_FST_MGMT , FC_FDMI_SUBTYPE , FC_FDMI_RPA ) ;
2012-11-15 22:41:18 +05:30
/* Prepare RPA payload */
pld = ( uint8_t * ) csio_ct_get_pld ( cmd ) ;
port_name = ( struct fc_fdmi_port_name * ) pld ;
memcpy ( & port_name - > portname , csio_ln_wwpn ( ln ) , 8 ) ;
pld + = sizeof ( * port_name ) ;
/* Start appending Port attributes */
attrib_blk = ( struct fs_fdmi_attrs * ) pld ;
attrib_blk - > numattrs = 0 ;
len + = sizeof ( attrib_blk - > numattrs ) ;
pld + = sizeof ( attrib_blk - > numattrs ) ;
fc4_type = & buf [ 0 ] ;
memset ( fc4_type , 0 , FC_FDMI_PORT_ATTR_FC4TYPES_LEN ) ;
fc4_type [ 2 ] = 1 ;
fc4_type [ 7 ] = 1 ;
csio_append_attrib ( & pld , FC_FDMI_PORT_ATTR_FC4TYPES ,
fc4_type , FC_FDMI_PORT_ATTR_FC4TYPES_LEN ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
val = htonl ( FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT ) ;
csio_append_attrib ( & pld , FC_FDMI_PORT_ATTR_SUPPORTEDSPEED ,
2017-05-09 15:34:44 -07:00
& val ,
2012-11-15 22:41:18 +05:30
FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
if ( hw - > pport [ ln - > portid ] . link_speed = = FW_PORT_CAP_SPEED_1G )
val = htonl ( FC_PORTSPEED_1GBIT ) ;
else if ( hw - > pport [ ln - > portid ] . link_speed = = FW_PORT_CAP_SPEED_10G )
val = htonl ( FC_PORTSPEED_10GBIT ) ;
else
val = htonl ( CSIO_HBA_PORTSPEED_UNKNOWN ) ;
csio_append_attrib ( & pld , FC_FDMI_PORT_ATTR_CURRENTPORTSPEED ,
2017-05-09 15:34:44 -07:00
& val , FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
2012-11-20 18:15:40 +05:30
mfs = ln - > ln_sparm . csp . sp_bb_data ;
2012-11-15 22:41:18 +05:30
csio_append_attrib ( & pld , FC_FDMI_PORT_ATTR_MAXFRAMESIZE ,
2017-05-09 15:34:44 -07:00
& mfs , sizeof ( mfs ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
strcpy ( buf , " csiostor " ) ;
csio_append_attrib ( & pld , FC_FDMI_PORT_ATTR_OSDEVICENAME , buf ,
2017-05-09 15:34:44 -07:00
strlen ( buf ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
if ( ! csio_hostname ( buf , sizeof ( buf ) ) ) {
csio_append_attrib ( & pld , FC_FDMI_PORT_ATTR_HOSTNAME ,
2017-05-09 15:34:44 -07:00
buf , strlen ( buf ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
}
2012-11-20 18:15:40 +05:30
attrib_blk - > numattrs = htonl ( numattrs ) ;
2012-11-15 22:41:18 +05:30
len = ( uint32_t ) ( pld - ( uint8_t * ) cmd ) ;
/* Submit FDMI RPA request */
spin_lock_irq ( & hw - > lock ) ;
if ( csio_ln_mgmt_submit_req ( fdmi_req , csio_ln_fdmi_done ,
FCOE_CT , & fdmi_req - > dma_buf , len ) ) {
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
csio_ln_dbg ( ln , " Failed to issue fdmi rpa req \n " ) ;
}
spin_unlock_irq ( & hw - > lock ) ;
}
/*
* csio_ln_fdmi_dprt_cbfn - DPRT completion
* @ hw : HW context
* @ fdmi_req : fdmi request
*/
static void
csio_ln_fdmi_dprt_cbfn ( struct csio_hw * hw , struct csio_ioreq * fdmi_req )
{
void * cmd ;
uint8_t * pld ;
uint32_t len = 0 ;
2012-11-20 18:15:40 +05:30
uint32_t numattrs = 0 ;
__be32 maxpayload = htonl ( 65536 ) ;
2012-11-15 22:41:18 +05:30
struct fc_fdmi_hba_identifier * hbaid ;
struct csio_lnode * ln = fdmi_req - > lnode ;
struct fc_fdmi_rpl * reg_pl ;
struct fs_fdmi_attrs * attrib_blk ;
uint8_t buf [ 64 ] ;
if ( fdmi_req - > wr_status ! = FW_SUCCESS ) {
csio_ln_dbg ( ln , " WR error:%x in processing fdmi dprt cmd \n " ,
fdmi_req - > wr_status ) ;
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
}
if ( ! csio_is_rnode_ready ( fdmi_req - > rnode ) ) {
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
return ;
}
cmd = fdmi_req - > dma_buf . vaddr ;
if ( ntohs ( csio_ct_rsp ( cmd ) ) ! = FC_FS_ACC ) {
csio_ln_dbg ( ln , " fdmi dprt cmd rejected reason %x expl %x \n " ,
csio_ct_reason ( cmd ) , csio_ct_expl ( cmd ) ) ;
}
/* Prepare CT hdr for RHBA cmd */
memset ( cmd , 0 , FC_CT_HDR_LEN ) ;
2012-11-20 18:15:40 +05:30
csio_fill_ct_iu ( cmd , FC_FST_MGMT , FC_FDMI_SUBTYPE , FC_FDMI_RHBA ) ;
2012-11-15 22:41:18 +05:30
len = FC_CT_HDR_LEN ;
/* Prepare RHBA payload */
pld = ( uint8_t * ) csio_ct_get_pld ( cmd ) ;
hbaid = ( struct fc_fdmi_hba_identifier * ) pld ;
memcpy ( & hbaid - > id , csio_ln_wwpn ( ln ) , 8 ) ; /* HBA identifer */
pld + = sizeof ( * hbaid ) ;
/* Register one port per hba */
reg_pl = ( struct fc_fdmi_rpl * ) pld ;
2012-11-20 18:15:40 +05:30
reg_pl - > numport = htonl ( 1 ) ;
2012-11-15 22:41:18 +05:30
memcpy ( & reg_pl - > port [ 0 ] . portname , csio_ln_wwpn ( ln ) , 8 ) ;
pld + = sizeof ( * reg_pl ) ;
/* Start appending HBA attributes hba */
attrib_blk = ( struct fs_fdmi_attrs * ) pld ;
attrib_blk - > numattrs = 0 ;
len + = sizeof ( attrib_blk - > numattrs ) ;
pld + = sizeof ( attrib_blk - > numattrs ) ;
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_NODENAME , csio_ln_wwnn ( ln ) ,
FC_FDMI_HBA_ATTR_NODENAME_LEN ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
memset ( buf , 0 , sizeof ( buf ) ) ;
strcpy ( buf , " Chelsio Communications " ) ;
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_MANUFACTURER , buf ,
2017-05-09 15:34:44 -07:00
strlen ( buf ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_SERIALNUMBER ,
2017-05-09 15:34:44 -07:00
hw - > vpd . sn , sizeof ( hw - > vpd . sn ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_MODEL , hw - > vpd . id ,
2017-05-09 15:34:44 -07:00
sizeof ( hw - > vpd . id ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_MODELDESCRIPTION ,
2017-05-09 15:34:44 -07:00
hw - > model_desc , strlen ( hw - > model_desc ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_HARDWAREVERSION ,
2017-05-09 15:34:44 -07:00
hw - > hw_ver , sizeof ( hw - > hw_ver ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_FIRMWAREVERSION ,
2017-05-09 15:34:44 -07:00
hw - > fwrev_str , strlen ( hw - > fwrev_str ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
if ( ! csio_osname ( buf , sizeof ( buf ) ) ) {
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_OSNAMEVERSION ,
2017-05-09 15:34:44 -07:00
buf , strlen ( buf ) ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
2012-11-15 22:41:18 +05:30
}
csio_append_attrib ( & pld , FC_FDMI_HBA_ATTR_MAXCTPAYLOAD ,
2017-05-09 15:34:44 -07:00
& maxpayload , FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN ) ;
2012-11-15 22:41:18 +05:30
len = ( uint32_t ) ( pld - ( uint8_t * ) cmd ) ;
2012-11-20 18:15:40 +05:30
numattrs + + ;
attrib_blk - > numattrs = htonl ( numattrs ) ;
2012-11-15 22:41:18 +05:30
/* Submit FDMI RHBA request */
spin_lock_irq ( & hw - > lock ) ;
if ( csio_ln_mgmt_submit_req ( fdmi_req , csio_ln_fdmi_rhba_cbfn ,
FCOE_CT , & fdmi_req - > dma_buf , len ) ) {
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
csio_ln_dbg ( ln , " Failed to issue fdmi rhba req \n " ) ;
}
spin_unlock_irq ( & hw - > lock ) ;
}
/*
* csio_ln_fdmi_dhba_cbfn - DHBA completion
* @ hw : HW context
* @ fdmi_req : fdmi request
*/
static void
csio_ln_fdmi_dhba_cbfn ( struct csio_hw * hw , struct csio_ioreq * fdmi_req )
{
struct csio_lnode * ln = fdmi_req - > lnode ;
void * cmd ;
struct fc_fdmi_port_name * port_name ;
uint32_t len ;
if ( fdmi_req - > wr_status ! = FW_SUCCESS ) {
csio_ln_dbg ( ln , " WR error:%x in processing fdmi dhba cmd \n " ,
fdmi_req - > wr_status ) ;
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
}
if ( ! csio_is_rnode_ready ( fdmi_req - > rnode ) ) {
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
return ;
}
cmd = fdmi_req - > dma_buf . vaddr ;
if ( ntohs ( csio_ct_rsp ( cmd ) ) ! = FC_FS_ACC ) {
csio_ln_dbg ( ln , " fdmi dhba cmd rejected reason %x expl %x \n " ,
csio_ct_reason ( cmd ) , csio_ct_expl ( cmd ) ) ;
}
/* Send FDMI cmd to de-register any Port attributes if registered
* before
*/
/* Prepare FDMI DPRT cmd */
memset ( cmd , 0 , FC_CT_HDR_LEN ) ;
2012-11-20 18:15:40 +05:30
csio_fill_ct_iu ( cmd , FC_FST_MGMT , FC_FDMI_SUBTYPE , FC_FDMI_DPRT ) ;
2012-11-15 22:41:18 +05:30
len = FC_CT_HDR_LEN ;
port_name = ( struct fc_fdmi_port_name * ) csio_ct_get_pld ( cmd ) ;
memcpy ( & port_name - > portname , csio_ln_wwpn ( ln ) , 8 ) ;
len + = sizeof ( * port_name ) ;
/* Submit FDMI request */
spin_lock_irq ( & hw - > lock ) ;
if ( csio_ln_mgmt_submit_req ( fdmi_req , csio_ln_fdmi_dprt_cbfn ,
FCOE_CT , & fdmi_req - > dma_buf , len ) ) {
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
csio_ln_dbg ( ln , " Failed to issue fdmi dprt req \n " ) ;
}
spin_unlock_irq ( & hw - > lock ) ;
}
/**
* csio_ln_fdmi_start - Start an FDMI request .
* @ ln : lnode
* @ context : session context
*
* Issued with lock held .
*/
int
csio_ln_fdmi_start ( struct csio_lnode * ln , void * context )
{
struct csio_ioreq * fdmi_req ;
struct csio_rnode * fdmi_rn = ( struct csio_rnode * ) context ;
void * cmd ;
struct fc_fdmi_hba_identifier * hbaid ;
uint32_t len ;
if ( ! ( ln - > flags & CSIO_LNF_FDMI_ENABLE ) )
return - EPROTONOSUPPORT ;
if ( ! csio_is_rnode_ready ( fdmi_rn ) )
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
/* Send FDMI cmd to de-register any HBA attributes if registered
* before
*/
fdmi_req = ln - > mgmt_req ;
fdmi_req - > lnode = ln ;
fdmi_req - > rnode = fdmi_rn ;
/* Prepare FDMI DHBA cmd */
cmd = fdmi_req - > dma_buf . vaddr ;
memset ( cmd , 0 , FC_CT_HDR_LEN ) ;
2012-11-20 18:15:40 +05:30
csio_fill_ct_iu ( cmd , FC_FST_MGMT , FC_FDMI_SUBTYPE , FC_FDMI_DHBA ) ;
2012-11-15 22:41:18 +05:30
len = FC_CT_HDR_LEN ;
hbaid = ( struct fc_fdmi_hba_identifier * ) csio_ct_get_pld ( cmd ) ;
memcpy ( & hbaid - > id , csio_ln_wwpn ( ln ) , 8 ) ;
len + = sizeof ( * hbaid ) ;
/* Submit FDMI request */
if ( csio_ln_mgmt_submit_req ( fdmi_req , csio_ln_fdmi_dhba_cbfn ,
FCOE_CT , & fdmi_req - > dma_buf , len ) ) {
CSIO_INC_STATS ( ln , n_fdmi_err ) ;
csio_ln_dbg ( ln , " Failed to issue fdmi dhba req \n " ) ;
}
return 0 ;
}
/*
* csio_ln_vnp_read_cbfn - vnp read completion handler .
* @ hw : HW lnode
* @ cbfn : Completion handler .
*
* Reads vnp response and updates ln parameters .
*/
static void
csio_ln_vnp_read_cbfn ( struct csio_hw * hw , struct csio_mb * mbp )
{
struct csio_lnode * ln = ( ( struct csio_lnode * ) mbp - > priv ) ;
struct fw_fcoe_vnp_cmd * rsp = ( struct fw_fcoe_vnp_cmd * ) ( mbp - > mb ) ;
struct fc_els_csp * csp ;
struct fc_els_cssp * clsp ;
enum fw_retval retval ;
2012-11-20 18:15:40 +05:30
__be32 nport_id ;
2012-11-15 22:41:18 +05:30
2014-11-07 09:35:25 +05:30
retval = FW_CMD_RETVAL_G ( ntohl ( rsp - > alloc_to_len16 ) ) ;
2012-11-15 22:41:18 +05:30
if ( retval ! = FW_SUCCESS ) {
csio_err ( hw , " FCOE VNP read cmd returned error:0x%x \n " , retval ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
return ;
}
spin_lock_irq ( & hw - > lock ) ;
memcpy ( ln - > mac , rsp - > vnport_mac , sizeof ( ln - > mac ) ) ;
2012-11-20 18:15:40 +05:30
memcpy ( & nport_id , & rsp - > vnport_mac [ 3 ] , sizeof ( uint8_t ) * 3 ) ;
ln - > nport_id = ntohl ( nport_id ) ;
ln - > nport_id = ln - > nport_id > > 8 ;
2012-11-15 22:41:18 +05:30
/* Update WWNs */
/*
* This may look like a duplication of what csio_fcoe_enable_link ( )
* does , but is absolutely necessary if the vnpi changes between
* a FCOE LINK UP and FCOE LINK DOWN .
*/
memcpy ( csio_ln_wwnn ( ln ) , rsp - > vnport_wwnn , 8 ) ;
memcpy ( csio_ln_wwpn ( ln ) , rsp - > vnport_wwpn , 8 ) ;
/* Copy common sparam */
csp = ( struct fc_els_csp * ) rsp - > cmn_srv_parms ;
ln - > ln_sparm . csp . sp_hi_ver = csp - > sp_hi_ver ;
ln - > ln_sparm . csp . sp_lo_ver = csp - > sp_lo_ver ;
2012-11-20 18:15:40 +05:30
ln - > ln_sparm . csp . sp_bb_cred = csp - > sp_bb_cred ;
ln - > ln_sparm . csp . sp_features = csp - > sp_features ;
ln - > ln_sparm . csp . sp_bb_data = csp - > sp_bb_data ;
ln - > ln_sparm . csp . sp_r_a_tov = csp - > sp_r_a_tov ;
ln - > ln_sparm . csp . sp_e_d_tov = csp - > sp_e_d_tov ;
2012-11-15 22:41:18 +05:30
/* Copy word 0 & word 1 of class sparam */
clsp = ( struct fc_els_cssp * ) rsp - > clsp_word_0_1 ;
2012-11-20 18:15:40 +05:30
ln - > ln_sparm . clsp [ 2 ] . cp_class = clsp - > cp_class ;
ln - > ln_sparm . clsp [ 2 ] . cp_init = clsp - > cp_init ;
ln - > ln_sparm . clsp [ 2 ] . cp_recip = clsp - > cp_recip ;
ln - > ln_sparm . clsp [ 2 ] . cp_rdfs = clsp - > cp_rdfs ;
2012-11-15 22:41:18 +05:30
spin_unlock_irq ( & hw - > lock ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
/* Send an event to update local attribs */
csio_lnode_async_event ( ln , CSIO_LN_FC_ATTRIB_UPDATE ) ;
}
/*
* csio_ln_vnp_read - Read vnp params .
* @ ln : lnode
* @ cbfn : Completion handler .
*
* Issued with lock held .
*/
static int
csio_ln_vnp_read ( struct csio_lnode * ln ,
void ( * cbfn ) ( struct csio_hw * , struct csio_mb * ) )
{
struct csio_hw * hw = ln - > hwp ;
struct csio_mb * mbp ;
/* Allocate Mbox request */
mbp = mempool_alloc ( hw - > mb_mempool , GFP_ATOMIC ) ;
if ( ! mbp ) {
CSIO_INC_STATS ( hw , n_err_nomem ) ;
return - ENOMEM ;
}
/* Prepare VNP Command */
csio_fcoe_vnp_read_init_mb ( ln , mbp ,
CSIO_MB_DEFAULT_TMO ,
ln - > fcf_flowid ,
ln - > vnp_flowid ,
cbfn ) ;
/* Issue MBOX cmd */
if ( csio_mb_issue ( hw , mbp ) ) {
csio_err ( hw , " Failed to issue mbox FCoE VNP command \n " ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
return - EINVAL ;
}
return 0 ;
}
/*
* csio_fcoe_enable_link - Enable fcoe link .
* @ ln : lnode
* @ enable : enable / disable
* Issued with lock held .
* Issues mbox cmd to bring up FCOE link on port associated with given ln .
*/
static int
csio_fcoe_enable_link ( struct csio_lnode * ln , bool enable )
{
struct csio_hw * hw = ln - > hwp ;
struct csio_mb * mbp ;
enum fw_retval retval ;
uint8_t portid ;
uint8_t sub_op ;
struct fw_fcoe_link_cmd * lcmd ;
int i ;
mbp = mempool_alloc ( hw - > mb_mempool , GFP_ATOMIC ) ;
if ( ! mbp ) {
CSIO_INC_STATS ( hw , n_err_nomem ) ;
return - ENOMEM ;
}
portid = ln - > portid ;
sub_op = enable ? FCOE_LINK_UP : FCOE_LINK_DOWN ;
csio_dbg ( hw , " bringing FCOE LINK %s on Port:%d \n " ,
sub_op ? " UP " : " DOWN " , portid ) ;
csio_write_fcoe_link_cond_init_mb ( ln , mbp , CSIO_MB_DEFAULT_TMO ,
portid , sub_op , 0 , 0 , 0 , NULL ) ;
if ( csio_mb_issue ( hw , mbp ) ) {
csio_err ( hw , " failed to issue FCOE LINK cmd on port[%d] \n " ,
portid ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
return - EINVAL ;
}
retval = csio_mb_fw_retval ( mbp ) ;
if ( retval ! = FW_SUCCESS ) {
csio_err ( hw ,
" FCOE LINK %s cmd on port[%d] failed with "
" ret:x%x \n " , sub_op ? " UP " : " DOWN " , portid , retval ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
return - EINVAL ;
}
if ( ! enable )
goto out ;
lcmd = ( struct fw_fcoe_link_cmd * ) mbp - > mb ;
memcpy ( csio_ln_wwnn ( ln ) , lcmd - > vnport_wwnn , 8 ) ;
memcpy ( csio_ln_wwpn ( ln ) , lcmd - > vnport_wwpn , 8 ) ;
for ( i = 0 ; i < CSIO_MAX_PPORTS ; i + + )
if ( hw - > pport [ i ] . portid = = portid )
memcpy ( hw - > pport [ i ] . mac , lcmd - > phy_mac , 6 ) ;
out :
mempool_free ( mbp , hw - > mb_mempool ) ;
return 0 ;
}
/*
* csio_ln_read_fcf_cbfn - Read fcf parameters
* @ ln : lnode
*
* read fcf response and Update ln fcf information .
*/
static void
csio_ln_read_fcf_cbfn ( struct csio_hw * hw , struct csio_mb * mbp )
{
struct csio_lnode * ln = ( struct csio_lnode * ) mbp - > priv ;
struct csio_fcf_info * fcf_info ;
struct fw_fcoe_fcf_cmd * rsp =
( struct fw_fcoe_fcf_cmd * ) ( mbp - > mb ) ;
enum fw_retval retval ;
2014-11-07 09:35:25 +05:30
retval = FW_CMD_RETVAL_G ( ntohl ( rsp - > retval_len16 ) ) ;
2012-11-15 22:41:18 +05:30
if ( retval ! = FW_SUCCESS ) {
csio_ln_err ( ln , " FCOE FCF cmd failed with ret x%x \n " ,
retval ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
return ;
}
spin_lock_irq ( & hw - > lock ) ;
fcf_info = ln - > fcfinfo ;
fcf_info - > priority = FW_FCOE_FCF_CMD_PRIORITY_GET (
ntohs ( rsp - > priority_pkd ) ) ;
fcf_info - > vf_id = ntohs ( rsp - > vf_id ) ;
fcf_info - > vlan_id = rsp - > vlan_id ;
fcf_info - > max_fcoe_size = ntohs ( rsp - > max_fcoe_size ) ;
fcf_info - > fka_adv = be32_to_cpu ( rsp - > fka_adv ) ;
fcf_info - > fcfi = FW_FCOE_FCF_CMD_FCFI_GET ( ntohl ( rsp - > op_to_fcfi ) ) ;
fcf_info - > fpma = FW_FCOE_FCF_CMD_FPMA_GET ( rsp - > fpma_to_portid ) ;
fcf_info - > spma = FW_FCOE_FCF_CMD_SPMA_GET ( rsp - > fpma_to_portid ) ;
fcf_info - > login = FW_FCOE_FCF_CMD_LOGIN_GET ( rsp - > fpma_to_portid ) ;
fcf_info - > portid = FW_FCOE_FCF_CMD_PORTID_GET ( rsp - > fpma_to_portid ) ;
memcpy ( fcf_info - > fc_map , rsp - > fc_map , sizeof ( fcf_info - > fc_map ) ) ;
memcpy ( fcf_info - > mac , rsp - > mac , sizeof ( fcf_info - > mac ) ) ;
memcpy ( fcf_info - > name_id , rsp - > name_id , sizeof ( fcf_info - > name_id ) ) ;
memcpy ( fcf_info - > fabric , rsp - > fabric , sizeof ( fcf_info - > fabric ) ) ;
memcpy ( fcf_info - > spma_mac , rsp - > spma_mac , sizeof ( fcf_info - > spma_mac ) ) ;
spin_unlock_irq ( & hw - > lock ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
}
/*
* csio_ln_read_fcf_entry - Read fcf entry .
* @ ln : lnode
* @ cbfn : Completion handler .
*
* Issued with lock held .
*/
static int
csio_ln_read_fcf_entry ( struct csio_lnode * ln ,
void ( * cbfn ) ( struct csio_hw * , struct csio_mb * ) )
{
struct csio_hw * hw = ln - > hwp ;
struct csio_mb * mbp ;
mbp = mempool_alloc ( hw - > mb_mempool , GFP_ATOMIC ) ;
if ( ! mbp ) {
CSIO_INC_STATS ( hw , n_err_nomem ) ;
return - ENOMEM ;
}
/* Get FCoE FCF information */
csio_fcoe_read_fcf_init_mb ( ln , mbp , CSIO_MB_DEFAULT_TMO ,
ln - > portid , ln - > fcf_flowid , cbfn ) ;
if ( csio_mb_issue ( hw , mbp ) ) {
csio_err ( hw , " failed to issue FCOE FCF cmd \n " ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
return - EINVAL ;
}
return 0 ;
}
/*
* csio_handle_link_up - Logical Linkup event .
* @ hw - HW module .
* @ portid - Physical port number
* @ fcfi - FCF index .
* @ vnpi - VNP index .
* Returns - none .
*
* This event is received from FW , when virtual link is established between
* Physical port [ ENode ] and FCF . If its new vnpi , then local node object is
* created on this FCF and set to [ ONLINE ] state .
* Lnode waits for FW_RDEV_CMD event to be received indicating that
* Fabric login is completed and lnode moves to [ READY ] state .
*
* This called with hw lock held
*/
static void
csio_handle_link_up ( struct csio_hw * hw , uint8_t portid , uint32_t fcfi ,
uint32_t vnpi )
{
struct csio_lnode * ln = NULL ;
/* Lookup lnode based on vnpi */
ln = csio_ln_lookup_by_vnpi ( hw , vnpi ) ;
if ( ! ln ) {
/* Pick lnode based on portid */
ln = csio_ln_lookup_by_portid ( hw , portid ) ;
if ( ! ln ) {
csio_err ( hw , " failed to lookup fcoe lnode on port:%d \n " ,
portid ) ;
CSIO_DB_ASSERT ( 0 ) ;
return ;
}
/* Check if lnode has valid vnp flowid */
if ( ln - > vnp_flowid ! = CSIO_INVALID_IDX ) {
/* New VN-Port */
spin_unlock_irq ( & hw - > lock ) ;
csio_lnode_alloc ( hw ) ;
spin_lock_irq ( & hw - > lock ) ;
if ( ! ln ) {
csio_err ( hw ,
" failed to allocate fcoe lnode "
" for port:%d vnpi:x%x \n " ,
portid , vnpi ) ;
CSIO_DB_ASSERT ( 0 ) ;
return ;
}
ln - > portid = portid ;
}
ln - > vnp_flowid = vnpi ;
ln - > dev_num & = ~ 0xFFFF ;
ln - > dev_num | = vnpi ;
}
/*Initialize fcfi */
ln - > fcf_flowid = fcfi ;
csio_info ( hw , " Port:%d - FCOE LINK UP \n " , portid ) ;
CSIO_INC_STATS ( ln , n_link_up ) ;
/* Send LINKUP event to SM */
csio_post_event ( & ln - > sm , CSIO_LNE_LINKUP ) ;
}
/*
* csio_post_event_rns
* @ ln - FCOE lnode
* @ evt - Given rnode event
* Returns - none
*
* Posts given rnode event to all FCOE rnodes connected with given Lnode .
* This routine is invoked when lnode receives LINK_DOWN / DOWN_LINK / CLOSE
* event .
*
* This called with hw lock held
*/
static void
csio_post_event_rns ( struct csio_lnode * ln , enum csio_rn_ev evt )
{
struct csio_rnode * rnhead = ( struct csio_rnode * ) & ln - > rnhead ;
struct list_head * tmp , * next ;
struct csio_rnode * rn ;
list_for_each_safe ( tmp , next , & rnhead - > sm . sm_list ) {
rn = ( struct csio_rnode * ) tmp ;
csio_post_event ( & rn - > sm , evt ) ;
}
}
/*
* csio_cleanup_rns
* @ ln - FCOE lnode
* Returns - none
*
* Frees all FCOE rnodes connected with given Lnode .
*
* This called with hw lock held
*/
static void
csio_cleanup_rns ( struct csio_lnode * ln )
{
struct csio_rnode * rnhead = ( struct csio_rnode * ) & ln - > rnhead ;
struct list_head * tmp , * next_rn ;
struct csio_rnode * rn ;
list_for_each_safe ( tmp , next_rn , & rnhead - > sm . sm_list ) {
rn = ( struct csio_rnode * ) tmp ;
csio_put_rnode ( ln , rn ) ;
}
}
/*
* csio_post_event_lns
* @ ln - FCOE lnode
* @ evt - Given lnode event
* Returns - none
*
* Posts given lnode event to all FCOE lnodes connected with given Lnode .
* This routine is invoked when lnode receives LINK_DOWN / DOWN_LINK / CLOSE
* event .
*
* This called with hw lock held
*/
static void
csio_post_event_lns ( struct csio_lnode * ln , enum csio_ln_ev evt )
{
struct list_head * tmp ;
struct csio_lnode * cln , * sln ;
/* If NPIV lnode, send evt only to that and return */
if ( csio_is_npiv_ln ( ln ) ) {
csio_post_event ( & ln - > sm , evt ) ;
return ;
}
sln = ln ;
/* Traverse children lnodes list and send evt */
list_for_each ( tmp , & sln - > cln_head ) {
cln = ( struct csio_lnode * ) tmp ;
csio_post_event ( & cln - > sm , evt ) ;
}
/* Send evt to parent lnode */
csio_post_event ( & ln - > sm , evt ) ;
}
/*
* csio_ln_down - Lcoal nport is down
* @ ln - FCOE Lnode
* Returns - none
*
* Sends LINK_DOWN events to Lnode and its associated NPIVs lnodes .
*
* This called with hw lock held
*/
static void
csio_ln_down ( struct csio_lnode * ln )
{
csio_post_event_lns ( ln , CSIO_LNE_LINK_DOWN ) ;
}
/*
* csio_handle_link_down - Logical Linkdown event .
* @ hw - HW module .
* @ portid - Physical port number
* @ fcfi - FCF index .
* @ vnpi - VNP index .
* Returns - none
*
* This event is received from FW , when virtual link goes down between
* Physical port [ ENode ] and FCF . Lnode and its associated NPIVs lnode hosted on
* this vnpi [ VN - Port ] will be de - instantiated .
*
* This called with hw lock held
*/
static void
csio_handle_link_down ( struct csio_hw * hw , uint8_t portid , uint32_t fcfi ,
uint32_t vnpi )
{
struct csio_fcf_info * fp ;
struct csio_lnode * ln ;
/* Lookup lnode based on vnpi */
ln = csio_ln_lookup_by_vnpi ( hw , vnpi ) ;
if ( ln ) {
fp = ln - > fcfinfo ;
CSIO_INC_STATS ( ln , n_link_down ) ;
/*Warn if linkdown received if lnode is not in ready state */
if ( ! csio_is_lnode_ready ( ln ) ) {
csio_ln_warn ( ln ,
" warn: FCOE link is already in offline "
" Ignoring Fcoe linkdown event on portid %d \n " ,
portid ) ;
CSIO_INC_STATS ( ln , n_evt_drop ) ;
return ;
}
/* Verify portid */
if ( fp - > portid ! = portid ) {
csio_ln_warn ( ln ,
" warn: FCOE linkdown recv with "
" invalid port %d \n " , portid ) ;
CSIO_INC_STATS ( ln , n_evt_drop ) ;
return ;
}
/* verify fcfi */
if ( ln - > fcf_flowid ! = fcfi ) {
csio_ln_warn ( ln ,
" warn: FCOE linkdown recv with "
" invalid fcfi x%x \n " , fcfi ) ;
CSIO_INC_STATS ( ln , n_evt_drop ) ;
return ;
}
csio_info ( hw , " Port:%d - FCOE LINK DOWN \n " , portid ) ;
/* Send LINK_DOWN event to lnode s/m */
csio_ln_down ( ln ) ;
return ;
} else {
csio_warn ( hw ,
" warn: FCOE linkdown recv with invalid vnpi x%x \n " ,
vnpi ) ;
CSIO_INC_STATS ( hw , n_evt_drop ) ;
}
}
/*
* csio_is_lnode_ready - Checks FCOE lnode is in ready state .
* @ ln : Lnode module
*
* Returns True if FCOE lnode is in ready state .
*/
int
csio_is_lnode_ready ( struct csio_lnode * ln )
{
return ( csio_get_state ( ln ) = = ( ( csio_sm_state_t ) csio_lns_ready ) ) ;
}
/*****************************************************************************/
/* START: Lnode SM */
/*****************************************************************************/
/*
* csio_lns_uninit - The request in uninit state .
* @ ln - FCOE lnode .
* @ evt - Event to be processed .
*
* Process the given lnode event which is currently in " uninit " state .
* Invoked with HW lock held .
* Return - none .
*/
static void
csio_lns_uninit ( struct csio_lnode * ln , enum csio_ln_ev evt )
{
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
struct csio_lnode * rln = hw - > rln ;
int rv ;
CSIO_INC_STATS ( ln , n_evt_sm [ evt ] ) ;
switch ( evt ) {
case CSIO_LNE_LINKUP :
csio_set_state ( & ln - > sm , csio_lns_online ) ;
/* Read FCF only for physical lnode */
if ( csio_is_phys_ln ( ln ) ) {
rv = csio_ln_read_fcf_entry ( ln ,
csio_ln_read_fcf_cbfn ) ;
if ( rv ! = 0 ) {
/* TODO: Send HW RESET event */
CSIO_INC_STATS ( ln , n_err ) ;
break ;
}
/* Add FCF record */
list_add_tail ( & ln - > fcfinfo - > list , & rln - > fcf_lsthead ) ;
}
rv = csio_ln_vnp_read ( ln , csio_ln_vnp_read_cbfn ) ;
if ( rv ! = 0 ) {
/* TODO: Send HW RESET event */
CSIO_INC_STATS ( ln , n_err ) ;
}
break ;
case CSIO_LNE_DOWN_LINK :
break ;
default :
csio_ln_dbg ( ln ,
" unexp ln event %d recv from did:x%x in "
" ln state[uninit]. \n " , evt , ln - > nport_id ) ;
CSIO_INC_STATS ( ln , n_evt_unexp ) ;
break ;
} /* switch event */
}
/*
* csio_lns_online - The request in online state .
* @ ln - FCOE lnode .
* @ evt - Event to be processed .
*
* Process the given lnode event which is currently in " online " state .
* Invoked with HW lock held .
* Return - none .
*/
static void
csio_lns_online ( struct csio_lnode * ln , enum csio_ln_ev evt )
{
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
CSIO_INC_STATS ( ln , n_evt_sm [ evt ] ) ;
switch ( evt ) {
case CSIO_LNE_LINKUP :
csio_ln_warn ( ln ,
" warn: FCOE link is up already "
" Ignoring linkup on port:%d \n " , ln - > portid ) ;
CSIO_INC_STATS ( ln , n_evt_drop ) ;
break ;
case CSIO_LNE_FAB_INIT_DONE :
csio_set_state ( & ln - > sm , csio_lns_ready ) ;
spin_unlock_irq ( & hw - > lock ) ;
csio_lnode_async_event ( ln , CSIO_LN_FC_LINKUP ) ;
spin_lock_irq ( & hw - > lock ) ;
break ;
case CSIO_LNE_LINK_DOWN :
/* Fall through */
case CSIO_LNE_DOWN_LINK :
csio_set_state ( & ln - > sm , csio_lns_uninit ) ;
if ( csio_is_phys_ln ( ln ) ) {
/* Remove FCF entry */
list_del_init ( & ln - > fcfinfo - > list ) ;
}
break ;
default :
csio_ln_dbg ( ln ,
" unexp ln event %d recv from did:x%x in "
" ln state[uninit]. \n " , evt , ln - > nport_id ) ;
CSIO_INC_STATS ( ln , n_evt_unexp ) ;
break ;
} /* switch event */
}
/*
* csio_lns_ready - The request in ready state .
* @ ln - FCOE lnode .
* @ evt - Event to be processed .
*
* Process the given lnode event which is currently in " ready " state .
* Invoked with HW lock held .
* Return - none .
*/
static void
csio_lns_ready ( struct csio_lnode * ln , enum csio_ln_ev evt )
{
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
CSIO_INC_STATS ( ln , n_evt_sm [ evt ] ) ;
switch ( evt ) {
case CSIO_LNE_FAB_INIT_DONE :
csio_ln_dbg ( ln ,
" ignoring event %d recv from did x%x "
" in ln state[ready]. \n " , evt , ln - > nport_id ) ;
CSIO_INC_STATS ( ln , n_evt_drop ) ;
break ;
case CSIO_LNE_LINK_DOWN :
csio_set_state ( & ln - > sm , csio_lns_offline ) ;
csio_post_event_rns ( ln , CSIO_RNFE_DOWN ) ;
spin_unlock_irq ( & hw - > lock ) ;
csio_lnode_async_event ( ln , CSIO_LN_FC_LINKDOWN ) ;
spin_lock_irq ( & hw - > lock ) ;
if ( csio_is_phys_ln ( ln ) ) {
/* Remove FCF entry */
list_del_init ( & ln - > fcfinfo - > list ) ;
}
break ;
case CSIO_LNE_DOWN_LINK :
csio_set_state ( & ln - > sm , csio_lns_offline ) ;
csio_post_event_rns ( ln , CSIO_RNFE_DOWN ) ;
/* Host need to issue aborts in case if FW has not returned
* WRs with status " ABORTED "
*/
spin_unlock_irq ( & hw - > lock ) ;
csio_lnode_async_event ( ln , CSIO_LN_FC_LINKDOWN ) ;
spin_lock_irq ( & hw - > lock ) ;
if ( csio_is_phys_ln ( ln ) ) {
/* Remove FCF entry */
list_del_init ( & ln - > fcfinfo - > list ) ;
}
break ;
case CSIO_LNE_CLOSE :
csio_set_state ( & ln - > sm , csio_lns_uninit ) ;
csio_post_event_rns ( ln , CSIO_RNFE_CLOSE ) ;
break ;
case CSIO_LNE_LOGO :
csio_set_state ( & ln - > sm , csio_lns_offline ) ;
csio_post_event_rns ( ln , CSIO_RNFE_DOWN ) ;
break ;
default :
csio_ln_dbg ( ln ,
" unexp ln event %d recv from did:x%x in "
" ln state[uninit]. \n " , evt , ln - > nport_id ) ;
CSIO_INC_STATS ( ln , n_evt_unexp ) ;
CSIO_DB_ASSERT ( 0 ) ;
break ;
} /* switch event */
}
/*
* csio_lns_offline - The request in offline state .
* @ ln - FCOE lnode .
* @ evt - Event to be processed .
*
* Process the given lnode event which is currently in " offline " state .
* Invoked with HW lock held .
* Return - none .
*/
static void
csio_lns_offline ( struct csio_lnode * ln , enum csio_ln_ev evt )
{
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
struct csio_lnode * rln = hw - > rln ;
int rv ;
CSIO_INC_STATS ( ln , n_evt_sm [ evt ] ) ;
switch ( evt ) {
case CSIO_LNE_LINKUP :
csio_set_state ( & ln - > sm , csio_lns_online ) ;
/* Read FCF only for physical lnode */
if ( csio_is_phys_ln ( ln ) ) {
rv = csio_ln_read_fcf_entry ( ln ,
csio_ln_read_fcf_cbfn ) ;
if ( rv ! = 0 ) {
/* TODO: Send HW RESET event */
CSIO_INC_STATS ( ln , n_err ) ;
break ;
}
/* Add FCF record */
list_add_tail ( & ln - > fcfinfo - > list , & rln - > fcf_lsthead ) ;
}
rv = csio_ln_vnp_read ( ln , csio_ln_vnp_read_cbfn ) ;
if ( rv ! = 0 ) {
/* TODO: Send HW RESET event */
CSIO_INC_STATS ( ln , n_err ) ;
}
break ;
case CSIO_LNE_LINK_DOWN :
case CSIO_LNE_DOWN_LINK :
case CSIO_LNE_LOGO :
csio_ln_dbg ( ln ,
" ignoring event %d recv from did x%x "
" in ln state[offline]. \n " , evt , ln - > nport_id ) ;
CSIO_INC_STATS ( ln , n_evt_drop ) ;
break ;
case CSIO_LNE_CLOSE :
csio_set_state ( & ln - > sm , csio_lns_uninit ) ;
csio_post_event_rns ( ln , CSIO_RNFE_CLOSE ) ;
break ;
default :
csio_ln_dbg ( ln ,
" unexp ln event %d recv from did:x%x in "
" ln state[offline] \n " , evt , ln - > nport_id ) ;
CSIO_INC_STATS ( ln , n_evt_unexp ) ;
CSIO_DB_ASSERT ( 0 ) ;
break ;
} /* switch event */
}
/*****************************************************************************/
/* END: Lnode SM */
/*****************************************************************************/
static void
csio_free_fcfinfo ( struct kref * kref )
{
struct csio_fcf_info * fcfinfo = container_of ( kref ,
struct csio_fcf_info , kref ) ;
kfree ( fcfinfo ) ;
}
/* Helper routines for attributes */
/*
* csio_lnode_state_to_str - Get current state of FCOE lnode .
* @ ln - lnode
* @ str - state of lnode .
*
*/
void
csio_lnode_state_to_str ( struct csio_lnode * ln , int8_t * str )
{
if ( csio_get_state ( ln ) = = ( ( csio_sm_state_t ) csio_lns_uninit ) ) {
strcpy ( str , " UNINIT " ) ;
return ;
}
if ( csio_get_state ( ln ) = = ( ( csio_sm_state_t ) csio_lns_ready ) ) {
strcpy ( str , " READY " ) ;
return ;
}
if ( csio_get_state ( ln ) = = ( ( csio_sm_state_t ) csio_lns_offline ) ) {
strcpy ( str , " OFFLINE " ) ;
return ;
}
strcpy ( str , " UNKNOWN " ) ;
} /* csio_lnode_state_to_str */
int
csio_get_phy_port_stats ( struct csio_hw * hw , uint8_t portid ,
struct fw_fcoe_port_stats * port_stats )
{
struct csio_mb * mbp ;
struct fw_fcoe_port_cmd_params portparams ;
enum fw_retval retval ;
int idx ;
mbp = mempool_alloc ( hw - > mb_mempool , GFP_ATOMIC ) ;
if ( ! mbp ) {
csio_err ( hw , " FCoE FCF PARAMS command out of memory! \n " ) ;
return - EINVAL ;
}
portparams . portid = portid ;
for ( idx = 1 ; idx < = 3 ; idx + + ) {
portparams . idx = ( idx - 1 ) * 6 + 1 ;
portparams . nstats = 6 ;
if ( idx = = 3 )
portparams . nstats = 4 ;
csio_fcoe_read_portparams_init_mb ( hw , mbp , CSIO_MB_DEFAULT_TMO ,
& portparams , NULL ) ;
if ( csio_mb_issue ( hw , mbp ) ) {
csio_err ( hw , " Issue of FCoE port params failed! \n " ) ;
mempool_free ( mbp , hw - > mb_mempool ) ;
return - EINVAL ;
}
csio_mb_process_portparams_rsp ( hw , mbp , & retval ,
& portparams , port_stats ) ;
}
mempool_free ( mbp , hw - > mb_mempool ) ;
return 0 ;
}
/*
* csio_ln_mgmt_wr_handler - Mgmt Work Request handler .
* @ wr - WR .
* @ len - WR len .
* This handler is invoked when an outstanding mgmt WR is completed .
* Its invoked in the context of FW event worker thread for every
* mgmt event received .
* Return - none .
*/
static void
csio_ln_mgmt_wr_handler ( struct csio_hw * hw , void * wr , uint32_t len )
{
struct csio_mgmtm * mgmtm = csio_hw_to_mgmtm ( hw ) ;
struct csio_ioreq * io_req = NULL ;
struct fw_fcoe_els_ct_wr * wr_cmd ;
wr_cmd = ( struct fw_fcoe_els_ct_wr * ) wr ;
if ( len < sizeof ( struct fw_fcoe_els_ct_wr ) ) {
csio_err ( mgmtm - > hw ,
" Invalid ELS CT WR length recvd, len:%x \n " , len ) ;
mgmtm - > stats . n_err + + ;
return ;
}
io_req = ( struct csio_ioreq * ) ( ( uintptr_t ) wr_cmd - > cookie ) ;
io_req - > wr_status = csio_wr_status ( wr_cmd ) ;
/* lookup ioreq exists in our active Q */
spin_lock_irq ( & hw - > lock ) ;
if ( csio_mgmt_req_lookup ( mgmtm , io_req ) ! = 0 ) {
csio_err ( mgmtm - > hw ,
" Error- Invalid IO handle recv in WR. handle: %p \n " ,
io_req ) ;
mgmtm - > stats . n_err + + ;
spin_unlock_irq ( & hw - > lock ) ;
return ;
}
mgmtm = csio_hw_to_mgmtm ( hw ) ;
/* Dequeue from active queue */
list_del_init ( & io_req - > sm . sm_list ) ;
mgmtm - > stats . n_active - - ;
spin_unlock_irq ( & hw - > lock ) ;
/* io_req will be freed by completion handler */
if ( io_req - > io_cbfn )
io_req - > io_cbfn ( hw , io_req ) ;
}
/**
* csio_fcoe_fwevt_handler - Event handler for Firmware FCoE events .
* @ hw : HW module
* @ cpl_op : CPL opcode
* @ cmd : FW cmd / WR .
*
* Process received FCoE cmd / WR event from FW .
*/
void
csio_fcoe_fwevt_handler ( struct csio_hw * hw , __u8 cpl_op , __be64 * cmd )
{
struct csio_lnode * ln ;
struct csio_rnode * rn ;
uint8_t portid , opcode = * ( uint8_t * ) cmd ;
struct fw_fcoe_link_cmd * lcmd ;
struct fw_wr_hdr * wr ;
struct fw_rdev_wr * rdev_wr ;
enum fw_fcoe_link_status lstatus ;
uint32_t fcfi , rdev_flowid , vnpi ;
enum csio_ln_ev evt ;
if ( cpl_op = = CPL_FW6_MSG & & opcode = = FW_FCOE_LINK_CMD ) {
lcmd = ( struct fw_fcoe_link_cmd * ) cmd ;
lstatus = lcmd - > lstatus ;
portid = FW_FCOE_LINK_CMD_PORTID_GET (
ntohl ( lcmd - > op_to_portid ) ) ;
fcfi = FW_FCOE_LINK_CMD_FCFI_GET ( ntohl ( lcmd - > sub_opcode_fcfi ) ) ;
vnpi = FW_FCOE_LINK_CMD_VNPI_GET ( ntohl ( lcmd - > vnpi_pkd ) ) ;
if ( lstatus = = FCOE_LINKUP ) {
/* HW lock here */
spin_lock_irq ( & hw - > lock ) ;
csio_handle_link_up ( hw , portid , fcfi , vnpi ) ;
spin_unlock_irq ( & hw - > lock ) ;
/* HW un lock here */
} else if ( lstatus = = FCOE_LINKDOWN ) {
/* HW lock here */
spin_lock_irq ( & hw - > lock ) ;
csio_handle_link_down ( hw , portid , fcfi , vnpi ) ;
spin_unlock_irq ( & hw - > lock ) ;
/* HW un lock here */
} else {
csio_warn ( hw , " Unexpected FCOE LINK status:0x%x \n " ,
2012-11-20 18:15:40 +05:30
lcmd - > lstatus ) ;
2012-11-15 22:41:18 +05:30
CSIO_INC_STATS ( hw , n_cpl_unexp ) ;
}
} else if ( cpl_op = = CPL_FW6_PLD ) {
wr = ( struct fw_wr_hdr * ) ( cmd + 4 ) ;
2014-11-07 09:35:25 +05:30
if ( FW_WR_OP_G ( be32_to_cpu ( wr - > hi ) )
2012-11-15 22:41:18 +05:30
= = FW_RDEV_WR ) {
rdev_wr = ( struct fw_rdev_wr * ) ( cmd + 4 ) ;
rdev_flowid = FW_RDEV_WR_FLOWID_GET (
ntohl ( rdev_wr - > alloc_to_len16 ) ) ;
vnpi = FW_RDEV_WR_ASSOC_FLOWID_GET (
ntohl ( rdev_wr - > flags_to_assoc_flowid ) ) ;
csio_dbg ( hw ,
" FW_RDEV_WR: flowid:x%x ev_cause:x%x "
" vnpi:0x%x \n " , rdev_flowid ,
rdev_wr - > event_cause , vnpi ) ;
if ( rdev_wr - > protocol ! = PROT_FCOE ) {
csio_err ( hw ,
" FW_RDEV_WR: invalid proto:x%x "
" received with flowid:x%x \n " ,
rdev_wr - > protocol ,
rdev_flowid ) ;
CSIO_INC_STATS ( hw , n_evt_drop ) ;
return ;
}
/* HW lock here */
spin_lock_irq ( & hw - > lock ) ;
ln = csio_ln_lookup_by_vnpi ( hw , vnpi ) ;
if ( ! ln ) {
csio_err ( hw ,
" FW_DEV_WR: invalid vnpi:x%x received "
" with flowid:x%x \n " , vnpi , rdev_flowid ) ;
CSIO_INC_STATS ( hw , n_evt_drop ) ;
goto out_pld ;
}
rn = csio_confirm_rnode ( ln , rdev_flowid ,
& rdev_wr - > u . fcoe_rdev ) ;
if ( ! rn ) {
csio_ln_dbg ( ln ,
" Failed to confirm rnode "
" for flowid:x%x \n " , rdev_flowid ) ;
CSIO_INC_STATS ( hw , n_evt_drop ) ;
goto out_pld ;
}
/* save previous event for debugging */
ln - > prev_evt = ln - > cur_evt ;
ln - > cur_evt = rdev_wr - > event_cause ;
CSIO_INC_STATS ( ln , n_evt_fw [ rdev_wr - > event_cause ] ) ;
/* Translate all the fabric events to lnode SM events */
evt = CSIO_FWE_TO_LNE ( rdev_wr - > event_cause ) ;
if ( evt ) {
csio_ln_dbg ( ln ,
" Posting event to lnode event:%d "
" cause:%d flowid:x%x \n " , evt ,
rdev_wr - > event_cause , rdev_flowid ) ;
csio_post_event ( & ln - > sm , evt ) ;
}
/* Handover event to rn SM here. */
csio_rnode_fwevt_handler ( rn , rdev_wr - > event_cause ) ;
out_pld :
spin_unlock_irq ( & hw - > lock ) ;
return ;
} else {
csio_warn ( hw , " unexpected WR op(0x%x) recv \n " ,
2014-11-07 09:35:25 +05:30
FW_WR_OP_G ( be32_to_cpu ( ( wr - > hi ) ) ) ) ;
2012-11-15 22:41:18 +05:30
CSIO_INC_STATS ( hw , n_cpl_unexp ) ;
}
} else if ( cpl_op = = CPL_FW6_MSG ) {
wr = ( struct fw_wr_hdr * ) ( cmd ) ;
2014-11-07 09:35:25 +05:30
if ( FW_WR_OP_G ( be32_to_cpu ( wr - > hi ) ) = = FW_FCOE_ELS_CT_WR ) {
2012-11-15 22:41:18 +05:30
csio_ln_mgmt_wr_handler ( hw , wr ,
sizeof ( struct fw_fcoe_els_ct_wr ) ) ;
} else {
csio_warn ( hw , " unexpected WR op(0x%x) recv \n " ,
2014-11-07 09:35:25 +05:30
FW_WR_OP_G ( be32_to_cpu ( ( wr - > hi ) ) ) ) ;
2012-11-15 22:41:18 +05:30
CSIO_INC_STATS ( hw , n_cpl_unexp ) ;
}
} else {
csio_warn ( hw , " unexpected CPL op(0x%x) recv \n " , opcode ) ;
CSIO_INC_STATS ( hw , n_cpl_unexp ) ;
}
}
/**
* csio_lnode_start - Kickstart lnode discovery .
* @ ln : lnode
*
* This routine kickstarts the discovery by issuing an FCOE_LINK ( up ) command .
*/
int
csio_lnode_start ( struct csio_lnode * ln )
{
int rv = 0 ;
if ( csio_is_phys_ln ( ln ) & & ! ( ln - > flags & CSIO_LNF_LINK_ENABLE ) ) {
rv = csio_fcoe_enable_link ( ln , 1 ) ;
ln - > flags | = CSIO_LNF_LINK_ENABLE ;
}
return rv ;
}
/**
* csio_lnode_stop - Stop the lnode .
* @ ln : lnode
*
* This routine is invoked by HW module to stop lnode and its associated NPIV
* lnodes .
*/
void
csio_lnode_stop ( struct csio_lnode * ln )
{
csio_post_event_lns ( ln , CSIO_LNE_DOWN_LINK ) ;
if ( csio_is_phys_ln ( ln ) & & ( ln - > flags & CSIO_LNF_LINK_ENABLE ) ) {
csio_fcoe_enable_link ( ln , 0 ) ;
ln - > flags & = ~ CSIO_LNF_LINK_ENABLE ;
}
csio_ln_dbg ( ln , " stopping ln :%p \n " , ln ) ;
}
/**
* csio_lnode_close - Close an lnode .
* @ ln : lnode
*
* This routine is invoked by HW module to close an lnode and its
* associated NPIV lnodes . Lnode and its associated NPIV lnodes are
* set to uninitialized state .
*/
void
csio_lnode_close ( struct csio_lnode * ln )
{
csio_post_event_lns ( ln , CSIO_LNE_CLOSE ) ;
if ( csio_is_phys_ln ( ln ) )
ln - > vnp_flowid = CSIO_INVALID_IDX ;
csio_ln_dbg ( ln , " closed ln :%p \n " , ln ) ;
}
/*
* csio_ln_prep_ecwr - Prepare ELS / CT WR .
* @ io_req - IO request .
* @ wr_len - WR len
* @ immd_len - WR immediate data
* @ sub_op - Sub opcode
* @ sid - source portid .
* @ did - destination portid
* @ flow_id - flowid
* @ fw_wr - ELS / CT WR to be prepared .
* Returns : 0 - on success
*/
static int
csio_ln_prep_ecwr ( struct csio_ioreq * io_req , uint32_t wr_len ,
uint32_t immd_len , uint8_t sub_op , uint32_t sid ,
uint32_t did , uint32_t flow_id , uint8_t * fw_wr )
{
struct fw_fcoe_els_ct_wr * wr ;
2012-11-20 18:15:40 +05:30
__be32 port_id ;
2012-11-15 22:41:18 +05:30
wr = ( struct fw_fcoe_els_ct_wr * ) fw_wr ;
2014-11-07 09:35:25 +05:30
wr - > op_immdlen = cpu_to_be32 ( FW_WR_OP_V ( FW_FCOE_ELS_CT_WR ) |
2012-11-15 22:41:18 +05:30
FW_FCOE_ELS_CT_WR_IMMDLEN ( immd_len ) ) ;
wr_len = DIV_ROUND_UP ( wr_len , 16 ) ;
2014-11-07 09:35:25 +05:30
wr - > flowid_len16 = cpu_to_be32 ( FW_WR_FLOWID_V ( flow_id ) |
FW_WR_LEN16_V ( wr_len ) ) ;
2012-11-15 22:41:18 +05:30
wr - > els_ct_type = sub_op ;
wr - > ctl_pri = 0 ;
wr - > cp_en_class = 0 ;
wr - > cookie = io_req - > fw_handle ;
2012-11-20 18:15:40 +05:30
wr - > iqid = cpu_to_be16 ( csio_q_physiqid (
io_req - > lnode - > hwp , io_req - > iq_idx ) ) ;
2012-11-15 22:41:18 +05:30
wr - > fl_to_sp = FW_FCOE_ELS_CT_WR_SP ( 1 ) ;
wr - > tmo_val = ( uint8_t ) io_req - > tmo ;
port_id = htonl ( sid ) ;
memcpy ( wr - > l_id , PORT_ID_PTR ( port_id ) , 3 ) ;
port_id = htonl ( did ) ;
memcpy ( wr - > r_id , PORT_ID_PTR ( port_id ) , 3 ) ;
/* Prepare RSP SGL */
wr - > rsp_dmalen = cpu_to_be32 ( io_req - > dma_buf . len ) ;
wr - > rsp_dmaaddr = cpu_to_be64 ( io_req - > dma_buf . paddr ) ;
return 0 ;
}
/*
* csio_ln_mgmt_submit_wr - Post elsct work request .
* @ mgmtm - mgmtm
* @ io_req - io request .
* @ sub_op - ELS or CT request type
* @ pld - Dma Payload buffer
* @ pld_len - Payload len
* Prepares ELSCT Work request and sents it to FW .
* Returns : 0 - on success
*/
static int
csio_ln_mgmt_submit_wr ( struct csio_mgmtm * mgmtm , struct csio_ioreq * io_req ,
uint8_t sub_op , struct csio_dma_buf * pld ,
uint32_t pld_len )
{
struct csio_wr_pair wrp ;
struct csio_lnode * ln = io_req - > lnode ;
struct csio_rnode * rn = io_req - > rnode ;
struct csio_hw * hw = mgmtm - > hw ;
uint8_t fw_wr [ 64 ] ;
struct ulptx_sgl dsgl ;
uint32_t wr_size = 0 ;
uint8_t im_len = 0 ;
uint32_t wr_off = 0 ;
int ret = 0 ;
/* Calculate WR Size for this ELS REQ */
wr_size = sizeof ( struct fw_fcoe_els_ct_wr ) ;
/* Send as immediate data if pld < 256 */
if ( pld_len < 256 ) {
wr_size + = ALIGN ( pld_len , 8 ) ;
im_len = ( uint8_t ) pld_len ;
} else
wr_size + = sizeof ( struct ulptx_sgl ) ;
/* Roundup WR size in units of 16 bytes */
wr_size = ALIGN ( wr_size , 16 ) ;
/* Get WR to send ELS REQ */
ret = csio_wr_get ( hw , mgmtm - > eq_idx , wr_size , & wrp ) ;
if ( ret ! = 0 ) {
csio_err ( hw , " Failed to get WR for ec_req %p ret:%d \n " ,
io_req , ret ) ;
return ret ;
}
/* Prepare Generic WR used by all ELS/CT cmd */
csio_ln_prep_ecwr ( io_req , wr_size , im_len , sub_op ,
ln - > nport_id , rn - > nport_id ,
csio_rn_flowid ( rn ) ,
& fw_wr [ 0 ] ) ;
/* Copy ELS/CT WR CMD */
csio_wr_copy_to_wrp ( & fw_wr [ 0 ] , & wrp , wr_off ,
sizeof ( struct fw_fcoe_els_ct_wr ) ) ;
wr_off + = sizeof ( struct fw_fcoe_els_ct_wr ) ;
/* Copy payload to Immediate section of WR */
if ( im_len )
csio_wr_copy_to_wrp ( pld - > vaddr , & wrp , wr_off , im_len ) ;
else {
/* Program DSGL to dma payload */
2014-11-12 17:15:57 -08:00
dsgl . cmd_nsge = htonl ( ULPTX_CMD_V ( ULP_TX_SC_DSGL ) |
2015-01-08 21:38:16 -08:00
ULPTX_MORE_F | ULPTX_NSGE_V ( 1 ) ) ;
2012-11-15 22:41:18 +05:30
dsgl . len0 = cpu_to_be32 ( pld_len ) ;
dsgl . addr0 = cpu_to_be64 ( pld - > paddr ) ;
csio_wr_copy_to_wrp ( & dsgl , & wrp , ALIGN ( wr_off , 8 ) ,
sizeof ( struct ulptx_sgl ) ) ;
}
/* Issue work request to xmit ELS/CT req to FW */
csio_wr_issue ( mgmtm - > hw , mgmtm - > eq_idx , false ) ;
return ret ;
}
/*
* csio_ln_mgmt_submit_req - Submit FCOE Mgmt request .
* @ io_req - IO Request
* @ io_cbfn - Completion handler .
* @ req_type - ELS or CT request type
* @ pld - Dma Payload buffer
* @ pld_len - Payload len
*
*
* This API used submit managment ELS / CT request .
* This called with hw lock held
* Returns : 0 - on success
* - ENOMEM - on error .
*/
static int
csio_ln_mgmt_submit_req ( struct csio_ioreq * io_req ,
void ( * io_cbfn ) ( struct csio_hw * , struct csio_ioreq * ) ,
enum fcoe_cmn_type req_type , struct csio_dma_buf * pld ,
uint32_t pld_len )
{
struct csio_hw * hw = csio_lnode_to_hw ( io_req - > lnode ) ;
struct csio_mgmtm * mgmtm = csio_hw_to_mgmtm ( hw ) ;
int rv ;
2017-05-09 15:34:44 -07:00
BUG_ON ( pld_len > pld - > len ) ;
2012-11-15 22:41:18 +05:30
io_req - > io_cbfn = io_cbfn ; /* Upper layer callback handler */
io_req - > fw_handle = ( uintptr_t ) ( io_req ) ;
io_req - > eq_idx = mgmtm - > eq_idx ;
io_req - > iq_idx = mgmtm - > iq_idx ;
rv = csio_ln_mgmt_submit_wr ( mgmtm , io_req , req_type , pld , pld_len ) ;
if ( rv = = 0 ) {
list_add_tail ( & io_req - > sm . sm_list , & mgmtm - > active_q ) ;
mgmtm - > stats . n_active + + ;
}
return rv ;
}
/*
* csio_ln_fdmi_init - FDMI Init entry point .
* @ ln : lnode
*/
static int
csio_ln_fdmi_init ( struct csio_lnode * ln )
{
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
struct csio_dma_buf * dma_buf ;
/* Allocate MGMT request required for FDMI */
ln - > mgmt_req = kzalloc ( sizeof ( struct csio_ioreq ) , GFP_KERNEL ) ;
if ( ! ln - > mgmt_req ) {
csio_ln_err ( ln , " Failed to alloc ioreq for FDMI \n " ) ;
CSIO_INC_STATS ( hw , n_err_nomem ) ;
return - ENOMEM ;
}
/* Allocate Dma buffers for FDMI response Payload */
dma_buf = & ln - > mgmt_req - > dma_buf ;
dma_buf - > len = 2048 ;
dma_buf - > vaddr = pci_alloc_consistent ( hw - > pdev , dma_buf - > len ,
& dma_buf - > paddr ) ;
if ( ! dma_buf - > vaddr ) {
csio_err ( hw , " Failed to alloc DMA buffer for FDMI! \n " ) ;
kfree ( ln - > mgmt_req ) ;
ln - > mgmt_req = NULL ;
return - ENOMEM ;
}
ln - > flags | = CSIO_LNF_FDMI_ENABLE ;
return 0 ;
}
/*
* csio_ln_fdmi_exit - FDMI exit entry point .
* @ ln : lnode
*/
static int
csio_ln_fdmi_exit ( struct csio_lnode * ln )
{
struct csio_dma_buf * dma_buf ;
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
if ( ! ln - > mgmt_req )
return 0 ;
dma_buf = & ln - > mgmt_req - > dma_buf ;
if ( dma_buf - > vaddr )
pci_free_consistent ( hw - > pdev , dma_buf - > len , dma_buf - > vaddr ,
dma_buf - > paddr ) ;
kfree ( ln - > mgmt_req ) ;
return 0 ;
}
int
csio_scan_done ( struct csio_lnode * ln , unsigned long ticks ,
unsigned long time , unsigned long max_scan_ticks ,
unsigned long delta_scan_ticks )
{
int rv = 0 ;
if ( time > = max_scan_ticks )
return 1 ;
if ( ! ln - > tgt_scan_tick )
ln - > tgt_scan_tick = ticks ;
if ( ( ( ticks - ln - > tgt_scan_tick ) > = delta_scan_ticks ) ) {
if ( ! ln - > last_scan_ntgts )
ln - > last_scan_ntgts = ln - > n_scsi_tgts ;
else {
if ( ln - > last_scan_ntgts = = ln - > n_scsi_tgts )
return 1 ;
ln - > last_scan_ntgts = ln - > n_scsi_tgts ;
}
ln - > tgt_scan_tick = ticks ;
}
return rv ;
}
/*
* csio_notify_lnodes :
* @ hw : HW module
* @ note : Notification
*
* Called from the HW SM to fan out notifications to the
* Lnode SM . Since the HW SM is entered with lock held ,
* there is no need to hold locks here .
*
*/
void
csio_notify_lnodes ( struct csio_hw * hw , enum csio_ln_notify note )
{
struct list_head * tmp ;
struct csio_lnode * ln ;
csio_dbg ( hw , " Notifying all nodes of event %d \n " , note ) ;
/* Traverse children lnodes list and send evt */
list_for_each ( tmp , & hw - > sln_head ) {
ln = ( struct csio_lnode * ) tmp ;
switch ( note ) {
case CSIO_LN_NOTIFY_HWREADY :
csio_lnode_start ( ln ) ;
break ;
case CSIO_LN_NOTIFY_HWRESET :
case CSIO_LN_NOTIFY_HWREMOVE :
csio_lnode_close ( ln ) ;
break ;
case CSIO_LN_NOTIFY_HWSTOP :
csio_lnode_stop ( ln ) ;
break ;
default :
break ;
}
}
}
/*
* csio_disable_lnodes :
* @ hw : HW module
* @ portid : port id
* @ disable : disable / enable flag .
* If disable = 1 , disables all lnode hosted on given physical port .
* otherwise enables all the lnodes on given phsysical port .
* This routine need to called with hw lock held .
*/
void
csio_disable_lnodes ( struct csio_hw * hw , uint8_t portid , bool disable )
{
struct list_head * tmp ;
struct csio_lnode * ln ;
csio_dbg ( hw , " Notifying event to all nodes of port:%d \n " , portid ) ;
/* Traverse sibling lnodes list and send evt */
list_for_each ( tmp , & hw - > sln_head ) {
ln = ( struct csio_lnode * ) tmp ;
if ( ln - > portid ! = portid )
continue ;
if ( disable )
csio_lnode_stop ( ln ) ;
else
csio_lnode_start ( ln ) ;
}
}
/*
* csio_ln_init - Initialize an lnode .
* @ ln : lnode
*
*/
static int
csio_ln_init ( struct csio_lnode * ln )
{
int rv = - EINVAL ;
struct csio_lnode * rln , * pln ;
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
csio_init_state ( & ln - > sm , csio_lns_uninit ) ;
ln - > vnp_flowid = CSIO_INVALID_IDX ;
ln - > fcf_flowid = CSIO_INVALID_IDX ;
if ( csio_is_root_ln ( ln ) ) {
/* This is the lnode used during initialization */
ln - > fcfinfo = kzalloc ( sizeof ( struct csio_fcf_info ) , GFP_KERNEL ) ;
if ( ! ln - > fcfinfo ) {
csio_ln_err ( ln , " Failed to alloc FCF record \n " ) ;
CSIO_INC_STATS ( hw , n_err_nomem ) ;
goto err ;
}
INIT_LIST_HEAD ( & ln - > fcf_lsthead ) ;
kref_init ( & ln - > fcfinfo - > kref ) ;
if ( csio_fdmi_enable & & csio_ln_fdmi_init ( ln ) )
goto err ;
} else { /* Either a non-root physical or a virtual lnode */
/*
* THe rest is common for non - root physical and NPIV lnodes .
* Just get references to all other modules
*/
rln = csio_root_lnode ( ln ) ;
if ( csio_is_npiv_ln ( ln ) ) {
/* NPIV */
pln = csio_parent_lnode ( ln ) ;
kref_get ( & pln - > fcfinfo - > kref ) ;
ln - > fcfinfo = pln - > fcfinfo ;
} else {
/* Another non-root physical lnode (FCF) */
ln - > fcfinfo = kzalloc ( sizeof ( struct csio_fcf_info ) ,
GFP_KERNEL ) ;
if ( ! ln - > fcfinfo ) {
csio_ln_err ( ln , " Failed to alloc FCF info \n " ) ;
CSIO_INC_STATS ( hw , n_err_nomem ) ;
goto err ;
}
kref_init ( & ln - > fcfinfo - > kref ) ;
if ( csio_fdmi_enable & & csio_ln_fdmi_init ( ln ) )
goto err ;
}
} /* if (!csio_is_root_ln(ln)) */
return 0 ;
err :
return rv ;
}
static void
csio_ln_exit ( struct csio_lnode * ln )
{
struct csio_lnode * pln ;
csio_cleanup_rns ( ln ) ;
if ( csio_is_npiv_ln ( ln ) ) {
pln = csio_parent_lnode ( ln ) ;
kref_put ( & pln - > fcfinfo - > kref , csio_free_fcfinfo ) ;
} else {
kref_put ( & ln - > fcfinfo - > kref , csio_free_fcfinfo ) ;
if ( csio_fdmi_enable )
csio_ln_fdmi_exit ( ln ) ;
}
ln - > fcfinfo = NULL ;
}
/**
* csio_lnode_init - Initialize the members of an lnode .
* @ ln : lnode
*
*/
int
csio_lnode_init ( struct csio_lnode * ln , struct csio_hw * hw ,
struct csio_lnode * pln )
{
int rv = - EINVAL ;
/* Link this lnode to hw */
csio_lnode_to_hw ( ln ) = hw ;
/* Link child to parent if child lnode */
if ( pln )
ln - > pln = pln ;
else
ln - > pln = NULL ;
/* Initialize scsi_tgt and timers to zero */
ln - > n_scsi_tgts = 0 ;
ln - > last_scan_ntgts = 0 ;
ln - > tgt_scan_tick = 0 ;
/* Initialize rnode list */
INIT_LIST_HEAD ( & ln - > rnhead ) ;
INIT_LIST_HEAD ( & ln - > cln_head ) ;
/* Initialize log level for debug */
ln - > params . log_level = hw - > params . log_level ;
if ( csio_ln_init ( ln ) )
goto err ;
/* Add lnode to list of sibling or children lnodes */
spin_lock_irq ( & hw - > lock ) ;
list_add_tail ( & ln - > sm . sm_list , pln ? & pln - > cln_head : & hw - > sln_head ) ;
if ( pln )
pln - > num_vports + + ;
spin_unlock_irq ( & hw - > lock ) ;
hw - > num_lns + + ;
return 0 ;
err :
csio_lnode_to_hw ( ln ) = NULL ;
return rv ;
}
/**
* csio_lnode_exit - De - instantiate an lnode .
* @ ln : lnode
*
*/
void
csio_lnode_exit ( struct csio_lnode * ln )
{
struct csio_hw * hw = csio_lnode_to_hw ( ln ) ;
csio_ln_exit ( ln ) ;
/* Remove this lnode from hw->sln_head */
spin_lock_irq ( & hw - > lock ) ;
list_del_init ( & ln - > sm . sm_list ) ;
/* If it is children lnode, decrement the
* counter in its parent lnode
*/
if ( ln - > pln )
ln - > pln - > num_vports - - ;
/* Update root lnode pointer */
if ( list_empty ( & hw - > sln_head ) )
hw - > rln = NULL ;
else
hw - > rln = ( struct csio_lnode * ) csio_list_next ( & hw - > sln_head ) ;
spin_unlock_irq ( & hw - > lock ) ;
csio_lnode_to_hw ( ln ) = NULL ;
hw - > num_lns - - ;
}