2008-12-10 02:10:24 +03:00
/*
* Copyright ( c ) 2007 - 2008 Intel Corporation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Maintained at www . Open - FCoE . org
*/
# include <linux/module.h>
# include <linux/version.h>
# include <linux/spinlock.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/if_ether.h>
# include <linux/if_vlan.h>
# include <linux/crc32.h>
# include <linux/cpu.h>
# include <linux/fs.h>
# include <linux/sysfs.h>
# include <linux/ctype.h>
# include <scsi/scsi_tcq.h>
# include <scsi/scsicam.h>
# include <scsi/scsi_transport.h>
# include <scsi/scsi_transport_fc.h>
# include <net/rtnetlink.h>
# include <scsi/fc/fc_encaps.h>
# include <scsi/libfc.h>
# include <scsi/fc_frame.h>
# include <scsi/libfcoe.h>
2009-03-17 21:42:24 +03:00
# include "fcoe.h"
2009-03-27 19:06:31 +03:00
2009-03-17 21:42:24 +03:00
static int debug_fcoe ;
2009-03-27 19:06:31 +03:00
2008-12-10 02:10:24 +03:00
MODULE_AUTHOR ( " Open-FCoE.org " ) ;
MODULE_DESCRIPTION ( " FCoE " ) ;
2009-03-17 21:42:13 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
2008-12-10 02:10:24 +03:00
/* fcoe host list */
LIST_HEAD ( fcoe_hostlist ) ;
DEFINE_RWLOCK ( fcoe_hostlist_lock ) ;
DEFINE_TIMER ( fcoe_timer , NULL , 0 , 0 ) ;
2009-03-17 21:41:35 +03:00
DEFINE_PER_CPU ( struct fcoe_percpu_s , fcoe_percpu ) ;
2008-12-10 02:10:24 +03:00
/* Function Prototyes */
2009-03-17 21:42:24 +03:00
static int fcoe_reset ( struct Scsi_Host * shost ) ;
static int fcoe_xmit ( struct fc_lport * , struct fc_frame * ) ;
static int fcoe_rcv ( struct sk_buff * , struct net_device * ,
struct packet_type * , struct net_device * ) ;
static int fcoe_percpu_receive_thread ( void * arg ) ;
static void fcoe_clean_pending_queue ( struct fc_lport * lp ) ;
static void fcoe_percpu_clean ( struct fc_lport * lp ) ;
static int fcoe_link_ok ( struct fc_lport * lp ) ;
static struct fc_lport * fcoe_hostlist_lookup ( const struct net_device * ) ;
static int fcoe_hostlist_add ( const struct fc_lport * ) ;
static int fcoe_hostlist_remove ( const struct fc_lport * ) ;
static struct Scsi_Host * fcoe_host_alloc ( struct scsi_host_template * , int ) ;
2008-12-10 02:10:24 +03:00
static int fcoe_check_wait_queue ( struct fc_lport * ) ;
static void fcoe_recv_flogi ( struct fcoe_softc * , struct fc_frame * , u8 * ) ;
static int fcoe_device_notification ( struct notifier_block * , ulong , void * ) ;
static void fcoe_dev_setup ( void ) ;
static void fcoe_dev_cleanup ( void ) ;
/* notification function from net device */
static struct notifier_block fcoe_notifier = {
. notifier_call = fcoe_device_notification ,
} ;
2009-03-27 19:06:31 +03:00
static struct scsi_transport_template * scsi_transport_fcoe_sw ;
struct fc_function_template fcoe_transport_function = {
. show_host_node_name = 1 ,
. show_host_port_name = 1 ,
. show_host_supported_classes = 1 ,
. show_host_supported_fc4s = 1 ,
. show_host_active_fc4s = 1 ,
. show_host_maxframe_size = 1 ,
. show_host_port_id = 1 ,
. show_host_supported_speeds = 1 ,
. get_host_speed = fc_get_host_speed ,
. show_host_speed = 1 ,
. show_host_port_type = 1 ,
. get_host_port_state = fc_get_host_port_state ,
. show_host_port_state = 1 ,
. show_host_symbolic_name = 1 ,
. dd_fcrport_size = sizeof ( struct fc_rport_libfc_priv ) ,
. show_rport_maxframe_size = 1 ,
. show_rport_supported_classes = 1 ,
. show_host_fabric_name = 1 ,
. show_starget_node_name = 1 ,
. show_starget_port_name = 1 ,
. show_starget_port_id = 1 ,
. set_rport_dev_loss_tmo = fc_set_rport_loss_tmo ,
. show_rport_dev_loss_tmo = 1 ,
. get_fc_host_stats = fc_get_host_stats ,
. issue_fc_host_lip = fcoe_reset ,
. terminate_rport_io = fc_rport_terminate_io ,
} ;
static struct scsi_host_template fcoe_shost_template = {
. module = THIS_MODULE ,
. name = " FCoE Driver " ,
. proc_name = FCOE_NAME ,
. queuecommand = fc_queuecommand ,
. eh_abort_handler = fc_eh_abort ,
. eh_device_reset_handler = fc_eh_device_reset ,
. eh_host_reset_handler = fc_eh_host_reset ,
. slave_alloc = fc_slave_alloc ,
. change_queue_depth = fc_change_queue_depth ,
. change_queue_type = fc_change_queue_type ,
. this_id = - 1 ,
. cmd_per_lun = 32 ,
. can_queue = FCOE_MAX_OUTSTANDING_COMMANDS ,
. use_clustering = ENABLE_CLUSTERING ,
. sg_tablesize = SG_ALL ,
. max_sectors = 0xffff ,
} ;
/**
* fcoe_lport_config ( ) - sets up the fc_lport
* @ lp : ptr to the fc_lport
* @ shost : ptr to the parent scsi host
*
* Returns : 0 for success
*/
static int fcoe_lport_config ( struct fc_lport * lp )
{
lp - > link_up = 0 ;
lp - > qfull = 0 ;
lp - > max_retry_count = 3 ;
lp - > e_d_tov = 2 * 1000 ; /* FC-FS default */
lp - > r_a_tov = 2 * 2 * 1000 ;
lp - > service_params = ( FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL ) ;
fc_lport_init_stats ( lp ) ;
/* lport fc_lport related configuration */
fc_lport_config ( lp ) ;
/* offload related configuration */
lp - > crc_offload = 0 ;
lp - > seq_offload = 0 ;
lp - > lro_enabled = 0 ;
lp - > lro_xid = 0 ;
lp - > lso_max = 0 ;
return 0 ;
}
/**
* fcoe_netdev_config ( ) - Set up netdev for SW FCoE
* @ lp : ptr to the fc_lport
* @ netdev : ptr to the associated netdevice struct
*
* Must be called after fcoe_lport_config ( ) as it will use lport mutex
*
* Returns : 0 for success
*/
static int fcoe_netdev_config ( struct fc_lport * lp , struct net_device * netdev )
{
u32 mfs ;
u64 wwnn , wwpn ;
struct fcoe_softc * fc ;
u8 flogi_maddr [ ETH_ALEN ] ;
/* Setup lport private data to point to fcoe softc */
fc = lport_priv ( lp ) ;
fc - > lp = lp ;
fc - > real_dev = netdev ;
fc - > phys_dev = netdev ;
/* Require support for get_pauseparam ethtool op. */
if ( netdev - > priv_flags & IFF_802_1Q_VLAN )
fc - > phys_dev = vlan_dev_real_dev ( netdev ) ;
/* Do not support for bonding device */
if ( ( fc - > real_dev - > priv_flags & IFF_MASTER_ALB ) | |
( fc - > real_dev - > priv_flags & IFF_SLAVE_INACTIVE ) | |
( fc - > real_dev - > priv_flags & IFF_MASTER_8023AD ) ) {
return - EOPNOTSUPP ;
}
/*
* Determine max frame size based on underlying device and optional
* user - configured limit . If the MFS is too low , fcoe_link_ok ( )
* will return 0 , so do this first .
*/
mfs = fc - > real_dev - > mtu - ( sizeof ( struct fcoe_hdr ) +
sizeof ( struct fcoe_crc_eof ) ) ;
if ( fc_set_mfs ( lp , mfs ) )
return - EINVAL ;
if ( ! fcoe_link_ok ( lp ) )
lp - > link_up = 1 ;
/* offload features support */
if ( fc - > real_dev - > features & NETIF_F_SG )
lp - > sg_supp = 1 ;
# ifdef NETIF_F_FCOE_CRC
if ( netdev - > features & NETIF_F_FCOE_CRC ) {
lp - > crc_offload = 1 ;
printk ( KERN_DEBUG " fcoe:%s supports FCCRC offload \n " ,
netdev - > name ) ;
}
# endif
# ifdef NETIF_F_FSO
if ( netdev - > features & NETIF_F_FSO ) {
lp - > seq_offload = 1 ;
lp - > lso_max = netdev - > gso_max_size ;
printk ( KERN_DEBUG " fcoe:%s supports LSO for max len 0x%x \n " ,
netdev - > name , lp - > lso_max ) ;
}
# endif
if ( netdev - > fcoe_ddp_xid ) {
lp - > lro_enabled = 1 ;
lp - > lro_xid = netdev - > fcoe_ddp_xid ;
printk ( KERN_DEBUG " fcoe:%s supports LRO for max xid 0x%x \n " ,
netdev - > name , lp - > lro_xid ) ;
}
skb_queue_head_init ( & fc - > fcoe_pending_queue ) ;
fc - > fcoe_pending_queue_active = 0 ;
/* setup Source Mac Address */
memcpy ( fc - > ctl_src_addr , fc - > real_dev - > dev_addr ,
fc - > real_dev - > addr_len ) ;
wwnn = fcoe_wwn_from_mac ( fc - > real_dev - > dev_addr , 1 , 0 ) ;
fc_set_wwnn ( lp , wwnn ) ;
/* XXX - 3rd arg needs to be vlan id */
wwpn = fcoe_wwn_from_mac ( fc - > real_dev - > dev_addr , 2 , 0 ) ;
fc_set_wwpn ( lp , wwpn ) ;
/*
* Add FCoE MAC address as second unicast MAC address
* or enter promiscuous mode if not capable of listening
* for multiple unicast MACs .
*/
rtnl_lock ( ) ;
memcpy ( flogi_maddr , ( u8 [ 6 ] ) FC_FCOE_FLOGI_MAC , ETH_ALEN ) ;
dev_unicast_add ( fc - > real_dev , flogi_maddr , ETH_ALEN ) ;
rtnl_unlock ( ) ;
/*
* setup the receive function from ethernet driver
* on the ethertype for the given device
*/
fc - > fcoe_packet_type . func = fcoe_rcv ;
fc - > fcoe_packet_type . type = __constant_htons ( ETH_P_FCOE ) ;
fc - > fcoe_packet_type . dev = fc - > real_dev ;
dev_add_pack ( & fc - > fcoe_packet_type ) ;
return 0 ;
}
/**
* fcoe_shost_config ( ) - Sets up fc_lport - > host
* @ lp : ptr to the fc_lport
* @ shost : ptr to the associated scsi host
* @ dev : device associated to scsi host
*
* Must be called after fcoe_lport_config ( ) and fcoe_netdev_config ( )
*
* Returns : 0 for success
*/
static int fcoe_shost_config ( struct fc_lport * lp , struct Scsi_Host * shost ,
struct device * dev )
{
int rc = 0 ;
/* lport scsi host config */
lp - > host = shost ;
lp - > host - > max_lun = FCOE_MAX_LUN ;
lp - > host - > max_id = FCOE_MAX_FCP_TARGET ;
lp - > host - > max_channel = 0 ;
lp - > host - > transportt = scsi_transport_fcoe_sw ;
/* add the new host to the SCSI-ml */
rc = scsi_add_host ( lp - > host , dev ) ;
if ( rc ) {
FC_DBG ( " fcoe_shost_config:error on scsi_add_host \n " ) ;
return rc ;
}
sprintf ( fc_host_symbolic_name ( lp - > host ) , " %s v%s over %s " ,
FCOE_NAME , FCOE_VERSION ,
fcoe_netdev ( lp ) - > name ) ;
return 0 ;
}
/**
* fcoe_em_config ( ) - allocates em for this lport
* @ lp : the port that em is to allocated for
*
* Returns : 0 on success
*/
static inline int fcoe_em_config ( struct fc_lport * lp )
{
BUG_ON ( lp - > emp ) ;
lp - > emp = fc_exch_mgr_alloc ( lp , FC_CLASS_3 ,
FCOE_MIN_XID , FCOE_MAX_XID ) ;
if ( ! lp - > emp )
return - ENOMEM ;
return 0 ;
}
/**
* fcoe_if_destroy ( ) - FCoE software HBA tear - down function
* @ netdev : ptr to the associated net_device
*
* Returns : 0 if link is OK for use by FCoE .
*/
static int fcoe_if_destroy ( struct net_device * netdev )
{
struct fc_lport * lp = NULL ;
struct fcoe_softc * fc ;
u8 flogi_maddr [ ETH_ALEN ] ;
BUG_ON ( ! netdev ) ;
printk ( KERN_DEBUG " fcoe_if_destroy:interface on %s \n " ,
netdev - > name ) ;
lp = fcoe_hostlist_lookup ( netdev ) ;
if ( ! lp )
return - ENODEV ;
fc = lport_priv ( lp ) ;
/* Logout of the fabric */
fc_fabric_logoff ( lp ) ;
/* Remove the instance from fcoe's list */
fcoe_hostlist_remove ( lp ) ;
/* Don't listen for Ethernet packets anymore */
dev_remove_pack ( & fc - > fcoe_packet_type ) ;
/* Cleanup the fc_lport */
fc_lport_destroy ( lp ) ;
fc_fcp_destroy ( lp ) ;
/* Detach from the scsi-ml */
fc_remove_host ( lp - > host ) ;
scsi_remove_host ( lp - > host ) ;
/* There are no more rports or I/O, free the EM */
if ( lp - > emp )
fc_exch_mgr_free ( lp - > emp ) ;
/* Delete secondary MAC addresses */
rtnl_lock ( ) ;
memcpy ( flogi_maddr , ( u8 [ 6 ] ) FC_FCOE_FLOGI_MAC , ETH_ALEN ) ;
dev_unicast_delete ( fc - > real_dev , flogi_maddr , ETH_ALEN ) ;
if ( compare_ether_addr ( fc - > data_src_addr , ( u8 [ 6 ] ) { 0 } ) )
dev_unicast_delete ( fc - > real_dev , fc - > data_src_addr , ETH_ALEN ) ;
rtnl_unlock ( ) ;
/* Free the per-CPU revieve threads */
fcoe_percpu_clean ( lp ) ;
/* Free existing skbs */
fcoe_clean_pending_queue ( lp ) ;
/* Free memory used by statistical counters */
fc_lport_free_stats ( lp ) ;
/* Release the net_device and Scsi_Host */
dev_put ( fc - > real_dev ) ;
scsi_host_put ( lp - > host ) ;
return 0 ;
}
/*
* fcoe_ddp_setup - calls LLD ' s ddp_setup through net_device
* @ lp : the corresponding fc_lport
* @ xid : the exchange id for this ddp transfer
* @ sgl : the scatterlist describing this transfer
* @ sgc : number of sg items
*
* Returns : 0 no ddp
*/
static int fcoe_ddp_setup ( struct fc_lport * lp , u16 xid ,
struct scatterlist * sgl , unsigned int sgc )
{
struct net_device * n = fcoe_netdev ( lp ) ;
if ( n - > netdev_ops & & n - > netdev_ops - > ndo_fcoe_ddp_setup )
return n - > netdev_ops - > ndo_fcoe_ddp_setup ( n , xid , sgl , sgc ) ;
return 0 ;
}
/*
* fcoe_ddp_done - calls LLD ' s ddp_done through net_device
* @ lp : the corresponding fc_lport
* @ xid : the exchange id for this ddp transfer
*
* Returns : the length of data that have been completed by ddp
*/
static int fcoe_ddp_done ( struct fc_lport * lp , u16 xid )
{
struct net_device * n = fcoe_netdev ( lp ) ;
if ( n - > netdev_ops & & n - > netdev_ops - > ndo_fcoe_ddp_done )
return n - > netdev_ops - > ndo_fcoe_ddp_done ( n , xid ) ;
return 0 ;
}
static struct libfc_function_template fcoe_libfc_fcn_templ = {
. frame_send = fcoe_xmit ,
. ddp_setup = fcoe_ddp_setup ,
. ddp_done = fcoe_ddp_done ,
} ;
/**
* fcoe_if_create ( ) - this function creates the fcoe interface
* @ netdev : pointer the associated netdevice
*
* Creates fc_lport struct and scsi_host for lport , configures lport
* and starts fabric login .
*
* Returns : 0 on success
*/
static int fcoe_if_create ( struct net_device * netdev )
{
int rc ;
struct fc_lport * lp = NULL ;
struct fcoe_softc * fc ;
struct Scsi_Host * shost ;
BUG_ON ( ! netdev ) ;
printk ( KERN_DEBUG " fcoe_if_create:interface on %s \n " ,
netdev - > name ) ;
lp = fcoe_hostlist_lookup ( netdev ) ;
if ( lp )
return - EEXIST ;
shost = fcoe_host_alloc ( & fcoe_shost_template ,
sizeof ( struct fcoe_softc ) ) ;
if ( ! shost ) {
FC_DBG ( " Could not allocate host structure \n " ) ;
return - ENOMEM ;
}
lp = shost_priv ( shost ) ;
fc = lport_priv ( lp ) ;
/* configure fc_lport, e.g., em */
rc = fcoe_lport_config ( lp ) ;
if ( rc ) {
FC_DBG ( " Could not configure lport \n " ) ;
goto out_host_put ;
}
/* configure lport network properties */
rc = fcoe_netdev_config ( lp , netdev ) ;
if ( rc ) {
FC_DBG ( " Could not configure netdev for lport \n " ) ;
goto out_host_put ;
}
/* configure lport scsi host properties */
rc = fcoe_shost_config ( lp , shost , & netdev - > dev ) ;
if ( rc ) {
FC_DBG ( " Could not configure shost for lport \n " ) ;
goto out_host_put ;
}
/* lport exch manager allocation */
rc = fcoe_em_config ( lp ) ;
if ( rc ) {
FC_DBG ( " Could not configure em for lport \n " ) ;
goto out_host_put ;
}
/* Initialize the library */
rc = fcoe_libfc_config ( lp , & fcoe_libfc_fcn_templ ) ;
if ( rc ) {
FC_DBG ( " Could not configure libfc for lport! \n " ) ;
goto out_lp_destroy ;
}
/* add to lports list */
fcoe_hostlist_add ( lp ) ;
lp - > boot_time = jiffies ;
fc_fabric_login ( lp ) ;
dev_hold ( netdev ) ;
return rc ;
out_lp_destroy :
fc_exch_mgr_free ( lp - > emp ) ; /* Free the EM */
out_host_put :
scsi_host_put ( lp - > host ) ;
return rc ;
}
/**
* fcoe_if_init ( ) - attach to scsi transport
*
* Returns : 0 on success
*/
static int __init fcoe_if_init ( void )
{
/* attach to scsi transport */
scsi_transport_fcoe_sw =
fc_attach_transport ( & fcoe_transport_function ) ;
if ( ! scsi_transport_fcoe_sw ) {
printk ( KERN_ERR " fcoe_init:fc_attach_transport() failed \n " ) ;
return - ENODEV ;
}
return 0 ;
}
/**
* fcoe_if_exit ( ) - detach from scsi transport
*
* Returns : 0 on success
*/
int __exit fcoe_if_exit ( void )
{
fc_release_transport ( scsi_transport_fcoe_sw ) ;
return 0 ;
}
2009-03-17 21:41:46 +03:00
/**
* fcoe_percpu_thread_create ( ) - Create a receive thread for an online cpu
* @ cpu : cpu index for the online cpu
*/
static void fcoe_percpu_thread_create ( unsigned int cpu )
{
struct fcoe_percpu_s * p ;
struct task_struct * thread ;
p = & per_cpu ( fcoe_percpu , cpu ) ;
thread = kthread_create ( fcoe_percpu_receive_thread ,
( void * ) p , " fcoethread/%d " , cpu ) ;
if ( likely ( ! IS_ERR ( p - > thread ) ) ) {
kthread_bind ( thread , cpu ) ;
wake_up_process ( thread ) ;
spin_lock_bh ( & p - > fcoe_rx_list . lock ) ;
p - > thread = thread ;
spin_unlock_bh ( & p - > fcoe_rx_list . lock ) ;
}
}
/**
* fcoe_percpu_thread_destroy ( ) - removes the rx thread for the given cpu
* @ cpu : cpu index the rx thread is to be removed
*
* Destroys a per - CPU Rx thread . Any pending skbs are moved to the
* current CPU ' s Rx thread . If the thread being destroyed is bound to
* the CPU processing this context the skbs will be freed .
*/
static void fcoe_percpu_thread_destroy ( unsigned int cpu )
{
struct fcoe_percpu_s * p ;
struct task_struct * thread ;
struct page * crc_eof ;
struct sk_buff * skb ;
# ifdef CONFIG_SMP
struct fcoe_percpu_s * p0 ;
unsigned targ_cpu = smp_processor_id ( ) ;
# endif /* CONFIG_SMP */
printk ( KERN_DEBUG " fcoe: Destroying receive thread for CPU %d \n " , cpu ) ;
/* Prevent any new skbs from being queued for this CPU. */
p = & per_cpu ( fcoe_percpu , cpu ) ;
spin_lock_bh ( & p - > fcoe_rx_list . lock ) ;
thread = p - > thread ;
p - > thread = NULL ;
crc_eof = p - > crc_eof_page ;
p - > crc_eof_page = NULL ;
p - > crc_eof_offset = 0 ;
spin_unlock_bh ( & p - > fcoe_rx_list . lock ) ;
# ifdef CONFIG_SMP
/*
* Don ' t bother moving the skb ' s if this context is running
* on the same CPU that is having its thread destroyed . This
* can easily happen when the module is removed .
*/
if ( cpu ! = targ_cpu ) {
p0 = & per_cpu ( fcoe_percpu , targ_cpu ) ;
spin_lock_bh ( & p0 - > fcoe_rx_list . lock ) ;
if ( p0 - > thread ) {
FC_DBG ( " Moving frames from CPU %d to CPU %d \n " ,
cpu , targ_cpu ) ;
while ( ( skb = __skb_dequeue ( & p - > fcoe_rx_list ) ) ! = NULL )
__skb_queue_tail ( & p0 - > fcoe_rx_list , skb ) ;
spin_unlock_bh ( & p0 - > fcoe_rx_list . lock ) ;
} else {
/*
* The targeted CPU is not initialized and cannot accept
* new skbs . Unlock the targeted CPU and drop the skbs
* on the CPU that is going offline .
*/
while ( ( skb = __skb_dequeue ( & p - > fcoe_rx_list ) ) ! = NULL )
kfree_skb ( skb ) ;
spin_unlock_bh ( & p0 - > fcoe_rx_list . lock ) ;
}
} else {
/*
* This scenario occurs when the module is being removed
* and all threads are being destroyed . skbs will continue
* to be shifted from the CPU thread that is being removed
* to the CPU thread associated with the CPU that is processing
* the module removal . Once there is only one CPU Rx thread it
* will reach this case and we will drop all skbs and later
* stop the thread .
*/
spin_lock_bh ( & p - > fcoe_rx_list . lock ) ;
while ( ( skb = __skb_dequeue ( & p - > fcoe_rx_list ) ) ! = NULL )
kfree_skb ( skb ) ;
spin_unlock_bh ( & p - > fcoe_rx_list . lock ) ;
}
# else
/*
* This a non - SMP scenario where the singluar Rx thread is
* being removed . Free all skbs and stop the thread .
*/
spin_lock_bh ( & p - > fcoe_rx_list . lock ) ;
while ( ( skb = __skb_dequeue ( & p - > fcoe_rx_list ) ) ! = NULL )
kfree_skb ( skb ) ;
spin_unlock_bh ( & p - > fcoe_rx_list . lock ) ;
# endif
if ( thread )
kthread_stop ( thread ) ;
if ( crc_eof )
put_page ( crc_eof ) ;
}
/**
* fcoe_cpu_callback ( ) - fcoe cpu hotplug event callback
* @ nfb : callback data block
* @ action : event triggering the callback
* @ hcpu : index for the cpu of this event
*
* This creates or destroys per cpu data for fcoe
*
* Returns NOTIFY_OK always .
*/
static int fcoe_cpu_callback ( struct notifier_block * nfb ,
unsigned long action , void * hcpu )
{
unsigned cpu = ( unsigned long ) hcpu ;
switch ( action ) {
case CPU_ONLINE :
case CPU_ONLINE_FROZEN :
FC_DBG ( " CPU %x online: Create Rx thread \n " , cpu ) ;
fcoe_percpu_thread_create ( cpu ) ;
break ;
case CPU_DEAD :
case CPU_DEAD_FROZEN :
FC_DBG ( " CPU %x offline: Remove Rx thread \n " , cpu ) ;
fcoe_percpu_thread_destroy ( cpu ) ;
break ;
default :
break ;
}
return NOTIFY_OK ;
}
static struct notifier_block fcoe_cpu_notifier = {
. notifier_call = fcoe_cpu_callback ,
} ;
2008-12-10 02:10:24 +03:00
/**
2009-02-27 21:55:45 +03:00
* fcoe_rcv ( ) - this is the fcoe receive function called by NET_RX_SOFTIRQ
2008-12-10 02:10:24 +03:00
* @ skb : the receive skb
* @ dev : associated net device
* @ ptype : context
* @ odldev : last device
*
* this function will receive the packet and build fc frame and pass it up
*
* Returns : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
int fcoe_rcv ( struct sk_buff * skb , struct net_device * dev ,
struct packet_type * ptype , struct net_device * olddev )
{
struct fc_lport * lp ;
struct fcoe_rcv_info * fr ;
struct fcoe_softc * fc ;
struct fc_frame_header * fh ;
struct fcoe_percpu_s * fps ;
2009-03-17 21:41:30 +03:00
unsigned short oxid ;
2009-03-17 21:41:46 +03:00
unsigned int cpu = 0 ;
2008-12-10 02:10:24 +03:00
fc = container_of ( ptype , struct fcoe_softc , fcoe_packet_type ) ;
lp = fc - > lp ;
if ( unlikely ( lp = = NULL ) ) {
FC_DBG ( " cannot find hba structure " ) ;
goto err2 ;
}
if ( unlikely ( debug_fcoe ) ) {
FC_DBG ( " skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
" end:%p sum:%d dev:%s " , skb - > len , skb - > data_len ,
skb - > head , skb - > data , skb_tail_pointer ( skb ) ,
skb_end_pointer ( skb ) , skb - > csum ,
skb - > dev ? skb - > dev - > name : " <NULL> " ) ;
}
/* check for FCOE packet type */
if ( unlikely ( eth_hdr ( skb ) - > h_proto ! = htons ( ETH_P_FCOE ) ) ) {
FC_DBG ( " wrong FC type frame " ) ;
goto err ;
}
/*
* Check for minimum frame length , and make sure required FCoE
* and FC headers are pulled into the linear data area .
*/
if ( unlikely ( ( skb - > len < FCOE_MIN_FRAME ) | |
! pskb_may_pull ( skb , FCOE_HEADER_LEN ) ) )
goto err ;
skb_set_transport_header ( skb , sizeof ( struct fcoe_hdr ) ) ;
fh = ( struct fc_frame_header * ) skb_transport_header ( skb ) ;
oxid = ntohs ( fh - > fh_ox_id ) ;
fr = fcoe_dev_from_skb ( skb ) ;
fr - > fr_dev = lp ;
fr - > ptype = ptype ;
2009-03-17 21:41:35 +03:00
2008-12-10 02:10:24 +03:00
# ifdef CONFIG_SMP
/*
* The incoming frame exchange id ( oxid ) is ANDed with num of online
2009-03-17 21:41:46 +03:00
* cpu bits to get cpu and then this cpu is used for selecting
* a per cpu kernel thread from fcoe_percpu .
2008-12-10 02:10:24 +03:00
*/
2009-03-17 21:41:46 +03:00
cpu = oxid & ( num_online_cpus ( ) - 1 ) ;
2008-12-10 02:10:24 +03:00
# endif
2009-03-17 21:41:35 +03:00
2009-03-17 21:41:46 +03:00
fps = & per_cpu ( fcoe_percpu , cpu ) ;
2008-12-10 02:10:24 +03:00
spin_lock_bh ( & fps - > fcoe_rx_list . lock ) ;
2009-03-17 21:41:46 +03:00
if ( unlikely ( ! fps - > thread ) ) {
/*
* The targeted CPU is not ready , let ' s target
* the first CPU now . For non - SMP systems this
* will check the same CPU twice .
*/
FC_DBG ( " CPU is online, but no receive thread ready "
" for incoming skb- using first online CPU. \n " ) ;
spin_unlock_bh ( & fps - > fcoe_rx_list . lock ) ;
cpu = first_cpu ( cpu_online_map ) ;
fps = & per_cpu ( fcoe_percpu , cpu ) ;
spin_lock_bh ( & fps - > fcoe_rx_list . lock ) ;
if ( ! fps - > thread ) {
spin_unlock_bh ( & fps - > fcoe_rx_list . lock ) ;
goto err ;
}
}
/*
* We now have a valid CPU that we ' re targeting for
* this skb . We also have this receive thread locked ,
* so we ' re free to queue skbs into it ' s queue .
*/
2008-12-10 02:10:24 +03:00
__skb_queue_tail ( & fps - > fcoe_rx_list , skb ) ;
if ( fps - > fcoe_rx_list . qlen = = 1 )
wake_up_process ( fps - > thread ) ;
spin_unlock_bh ( & fps - > fcoe_rx_list . lock ) ;
return 0 ;
err :
2009-04-01 02:51:50 +04:00
fc_lport_get_stats ( lp ) - > ErrorFrames + + ;
2008-12-10 02:10:24 +03:00
err2 :
kfree_skb ( skb ) ;
return - 1 ;
}
EXPORT_SYMBOL_GPL ( fcoe_rcv ) ;
/**
2009-02-27 21:55:45 +03:00
* fcoe_start_io ( ) - pass to netdev to start xmit for fcoe
2008-12-10 02:10:24 +03:00
* @ skb : the skb to be xmitted
*
* Returns : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static inline int fcoe_start_io ( struct sk_buff * skb )
{
int rc ;
skb_get ( skb ) ;
rc = dev_queue_xmit ( skb ) ;
if ( rc ! = 0 )
return rc ;
kfree_skb ( skb ) ;
return 0 ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_get_paged_crc_eof ( ) - in case we need alloc a page for crc_eof
2008-12-10 02:10:24 +03:00
* @ skb : the skb to be xmitted
* @ tlen : total len
*
* Returns : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static int fcoe_get_paged_crc_eof ( struct sk_buff * skb , int tlen )
{
struct fcoe_percpu_s * fps ;
struct page * page ;
2009-03-17 21:41:35 +03:00
fps = & get_cpu_var ( fcoe_percpu ) ;
2008-12-10 02:10:24 +03:00
page = fps - > crc_eof_page ;
if ( ! page ) {
page = alloc_page ( GFP_ATOMIC ) ;
if ( ! page ) {
2009-03-17 21:41:35 +03:00
put_cpu_var ( fcoe_percpu ) ;
2008-12-10 02:10:24 +03:00
return - ENOMEM ;
}
fps - > crc_eof_page = page ;
2009-03-17 21:41:46 +03:00
fps - > crc_eof_offset = 0 ;
2008-12-10 02:10:24 +03:00
}
get_page ( page ) ;
skb_fill_page_desc ( skb , skb_shinfo ( skb ) - > nr_frags , page ,
fps - > crc_eof_offset , tlen ) ;
skb - > len + = tlen ;
skb - > data_len + = tlen ;
skb - > truesize + = tlen ;
fps - > crc_eof_offset + = sizeof ( struct fcoe_crc_eof ) ;
if ( fps - > crc_eof_offset > = PAGE_SIZE ) {
fps - > crc_eof_page = NULL ;
fps - > crc_eof_offset = 0 ;
put_page ( page ) ;
}
2009-03-17 21:41:35 +03:00
put_cpu_var ( fcoe_percpu ) ;
2008-12-10 02:10:24 +03:00
return 0 ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_fc_crc ( ) - calculates FC CRC in this fcoe skb
2008-12-10 02:10:24 +03:00
* @ fp : the fc_frame containg data to be checksummed
*
* This uses crc32 ( ) to calculate the crc for fc frame
* Return : 32 bit crc
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
u32 fcoe_fc_crc ( struct fc_frame * fp )
{
struct sk_buff * skb = fp_skb ( fp ) ;
struct skb_frag_struct * frag ;
unsigned char * data ;
unsigned long off , len , clen ;
u32 crc ;
unsigned i ;
crc = crc32 ( ~ 0 , skb - > data , skb_headlen ( skb ) ) ;
for ( i = 0 ; i < skb_shinfo ( skb ) - > nr_frags ; i + + ) {
frag = & skb_shinfo ( skb ) - > frags [ i ] ;
off = frag - > page_offset ;
len = frag - > size ;
while ( len > 0 ) {
clen = min ( len , PAGE_SIZE - ( off & ~ PAGE_MASK ) ) ;
data = kmap_atomic ( frag - > page + ( off > > PAGE_SHIFT ) ,
KM_SKB_DATA_SOFTIRQ ) ;
crc = crc32 ( crc , data + ( off & ~ PAGE_MASK ) , clen ) ;
kunmap_atomic ( data , KM_SKB_DATA_SOFTIRQ ) ;
off + = clen ;
len - = clen ;
}
}
return crc ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_xmit ( ) - FCoE frame transmit function
2008-12-10 02:10:24 +03:00
* @ lp : the associated local port
* @ fp : the fc_frame to be transmitted
*
* Return : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
int fcoe_xmit ( struct fc_lport * lp , struct fc_frame * fp )
{
int wlen , rc = 0 ;
u32 crc ;
struct ethhdr * eh ;
struct fcoe_crc_eof * cp ;
struct sk_buff * skb ;
struct fcoe_dev_stats * stats ;
struct fc_frame_header * fh ;
unsigned int hlen ; /* header length implies the version */
unsigned int tlen ; /* trailer length */
unsigned int elen ; /* eth header, may include vlan */
int flogi_in_progress = 0 ;
struct fcoe_softc * fc ;
u8 sof , eof ;
struct fcoe_hdr * hp ;
WARN_ON ( ( fr_len ( fp ) % sizeof ( u32 ) ) ! = 0 ) ;
2009-02-27 21:55:55 +03:00
fc = lport_priv ( lp ) ;
2008-12-10 02:10:24 +03:00
/*
* if it is a flogi then we need to learn gw - addr
* and my own fcid
*/
fh = fc_frame_header_get ( fp ) ;
if ( unlikely ( fh - > fh_r_ctl = = FC_RCTL_ELS_REQ ) ) {
if ( fc_frame_payload_op ( fp ) = = ELS_FLOGI ) {
fc - > flogi_oxid = ntohs ( fh - > fh_ox_id ) ;
fc - > address_mode = FCOE_FCOUI_ADDR_MODE ;
fc - > flogi_progress = 1 ;
flogi_in_progress = 1 ;
} else if ( fc - > flogi_progress & & ntoh24 ( fh - > fh_s_id ) ! = 0 ) {
/*
* Here we must ' ve gotten an SID by accepting an FLOGI
* from a point - to - point connection . Switch to using
* the source mac based on the SID . The destination
* MAC in this case would have been set by receving the
* FLOGI .
*/
fc_fcoe_set_mac ( fc - > data_src_addr , fh - > fh_s_id ) ;
fc - > flogi_progress = 0 ;
}
}
skb = fp_skb ( fp ) ;
sof = fr_sof ( fp ) ;
eof = fr_eof ( fp ) ;
elen = ( fc - > real_dev - > priv_flags & IFF_802_1Q_VLAN ) ?
sizeof ( struct vlan_ethhdr ) : sizeof ( struct ethhdr ) ;
hlen = sizeof ( struct fcoe_hdr ) ;
tlen = sizeof ( struct fcoe_crc_eof ) ;
wlen = ( skb - > len - tlen + sizeof ( crc ) ) / FCOE_WORD_TO_BYTE ;
/* crc offload */
if ( likely ( lp - > crc_offload ) ) {
2009-02-28 01:07:15 +03:00
skb - > ip_summed = CHECKSUM_PARTIAL ;
2008-12-10 02:10:24 +03:00
skb - > csum_start = skb_headroom ( skb ) ;
skb - > csum_offset = skb - > len ;
crc = 0 ;
} else {
skb - > ip_summed = CHECKSUM_NONE ;
crc = fcoe_fc_crc ( fp ) ;
}
/* copy fc crc and eof to the skb buff */
if ( skb_is_nonlinear ( skb ) ) {
skb_frag_t * frag ;
if ( fcoe_get_paged_crc_eof ( skb , tlen ) ) {
2009-02-27 21:56:22 +03:00
kfree_skb ( skb ) ;
2008-12-10 02:10:24 +03:00
return - ENOMEM ;
}
frag = & skb_shinfo ( skb ) - > frags [ skb_shinfo ( skb ) - > nr_frags - 1 ] ;
cp = kmap_atomic ( frag - > page , KM_SKB_DATA_SOFTIRQ )
+ frag - > page_offset ;
} else {
cp = ( struct fcoe_crc_eof * ) skb_put ( skb , tlen ) ;
}
memset ( cp , 0 , sizeof ( * cp ) ) ;
cp - > fcoe_eof = eof ;
cp - > fcoe_crc32 = cpu_to_le32 ( ~ crc ) ;
if ( skb_is_nonlinear ( skb ) ) {
kunmap_atomic ( cp , KM_SKB_DATA_SOFTIRQ ) ;
cp = NULL ;
}
/* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
skb_push ( skb , elen + hlen ) ;
skb_reset_mac_header ( skb ) ;
skb_reset_network_header ( skb ) ;
skb - > mac_len = elen ;
2009-02-28 01:06:37 +03:00
skb - > protocol = htons ( ETH_P_FCOE ) ;
2008-12-10 02:10:24 +03:00
skb - > dev = fc - > real_dev ;
/* fill up mac and fcoe headers */
eh = eth_hdr ( skb ) ;
eh - > h_proto = htons ( ETH_P_FCOE ) ;
if ( fc - > address_mode = = FCOE_FCOUI_ADDR_MODE )
fc_fcoe_set_mac ( eh - > h_dest , fh - > fh_d_id ) ;
else
/* insert GW address */
memcpy ( eh - > h_dest , fc - > dest_addr , ETH_ALEN ) ;
if ( unlikely ( flogi_in_progress ) )
memcpy ( eh - > h_source , fc - > ctl_src_addr , ETH_ALEN ) ;
else
memcpy ( eh - > h_source , fc - > data_src_addr , ETH_ALEN ) ;
hp = ( struct fcoe_hdr * ) ( eh + 1 ) ;
memset ( hp , 0 , sizeof ( * hp ) ) ;
if ( FC_FCOE_VER )
FC_FCOE_ENCAPS_VER ( hp , FC_FCOE_VER ) ;
hp - > fcoe_sof = sof ;
2009-02-28 01:07:15 +03:00
# ifdef NETIF_F_FSO
/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
if ( lp - > seq_offload & & fr_max_payload ( fp ) ) {
skb_shinfo ( skb ) - > gso_type = SKB_GSO_FCOE ;
skb_shinfo ( skb ) - > gso_size = fr_max_payload ( fp ) ;
} else {
skb_shinfo ( skb ) - > gso_type = 0 ;
skb_shinfo ( skb ) - > gso_size = 0 ;
}
# endif
2008-12-10 02:10:24 +03:00
/* update tx stats: regardless if LLD fails */
2009-04-01 02:51:50 +04:00
stats = fc_lport_get_stats ( lp ) ;
stats - > TxFrames + + ;
stats - > TxWords + = wlen ;
2008-12-10 02:10:24 +03:00
/* send down to lld */
fr_dev ( fp ) = lp ;
if ( fc - > fcoe_pending_queue . qlen )
rc = fcoe_check_wait_queue ( lp ) ;
if ( rc = = 0 )
rc = fcoe_start_io ( skb ) ;
if ( rc ) {
2009-02-27 21:56:32 +03:00
spin_lock_bh ( & fc - > fcoe_pending_queue . lock ) ;
__skb_queue_tail ( & fc - > fcoe_pending_queue , skb ) ;
spin_unlock_bh ( & fc - > fcoe_pending_queue . lock ) ;
2008-12-10 02:10:24 +03:00
if ( fc - > fcoe_pending_queue . qlen > FCOE_MAX_QUEUE_DEPTH )
2009-02-27 21:54:57 +03:00
lp - > qfull = 1 ;
2008-12-10 02:10:24 +03:00
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( fcoe_xmit ) ;
2009-02-27 21:55:45 +03:00
/**
* fcoe_percpu_receive_thread ( ) - recv thread per cpu
2008-12-10 02:10:24 +03:00
* @ arg : ptr to the fcoe per cpu struct
*
* Return : 0 for success
*/
int fcoe_percpu_receive_thread ( void * arg )
{
struct fcoe_percpu_s * p = arg ;
u32 fr_len ;
struct fc_lport * lp ;
struct fcoe_rcv_info * fr ;
struct fcoe_dev_stats * stats ;
struct fc_frame_header * fh ;
struct sk_buff * skb ;
struct fcoe_crc_eof crc_eof ;
struct fc_frame * fp ;
u8 * mac = NULL ;
struct fcoe_softc * fc ;
struct fcoe_hdr * hp ;
2009-02-27 21:56:38 +03:00
set_user_nice ( current , - 20 ) ;
2008-12-10 02:10:24 +03:00
while ( ! kthread_should_stop ( ) ) {
spin_lock_bh ( & p - > fcoe_rx_list . lock ) ;
while ( ( skb = __skb_dequeue ( & p - > fcoe_rx_list ) ) = = NULL ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
spin_unlock_bh ( & p - > fcoe_rx_list . lock ) ;
schedule ( ) ;
set_current_state ( TASK_RUNNING ) ;
if ( kthread_should_stop ( ) )
return 0 ;
spin_lock_bh ( & p - > fcoe_rx_list . lock ) ;
}
spin_unlock_bh ( & p - > fcoe_rx_list . lock ) ;
fr = fcoe_dev_from_skb ( skb ) ;
lp = fr - > fr_dev ;
if ( unlikely ( lp = = NULL ) ) {
FC_DBG ( " invalid HBA Structure " ) ;
kfree_skb ( skb ) ;
continue ;
}
if ( unlikely ( debug_fcoe ) ) {
FC_DBG ( " skb_info: len:%d data_len:%d head:%p data:%p "
" tail:%p end:%p sum:%d dev:%s " ,
skb - > len , skb - > data_len ,
skb - > head , skb - > data , skb_tail_pointer ( skb ) ,
skb_end_pointer ( skb ) , skb - > csum ,
skb - > dev ? skb - > dev - > name : " <NULL> " ) ;
}
/*
* Save source MAC address before discarding header .
*/
fc = lport_priv ( lp ) ;
if ( unlikely ( fc - > flogi_progress ) )
mac = eth_hdr ( skb ) - > h_source ;
if ( skb_is_nonlinear ( skb ) )
skb_linearize ( skb ) ; /* not ideal */
/*
* Frame length checks and setting up the header pointers
* was done in fcoe_rcv already .
*/
hp = ( struct fcoe_hdr * ) skb_network_header ( skb ) ;
fh = ( struct fc_frame_header * ) skb_transport_header ( skb ) ;
2009-04-01 02:51:50 +04:00
stats = fc_lport_get_stats ( lp ) ;
2008-12-10 02:10:24 +03:00
if ( unlikely ( FC_FCOE_DECAPS_VER ( hp ) ! = FC_FCOE_VER ) ) {
2009-04-01 02:51:50 +04:00
if ( stats - > ErrorFrames < 5 )
printk ( KERN_WARNING " FCoE version "
" mismatch: The frame has "
" version %x, but the "
" initiator supports version "
" %x \n " , FC_FCOE_DECAPS_VER ( hp ) ,
FC_FCOE_VER ) ;
stats - > ErrorFrames + + ;
2008-12-10 02:10:24 +03:00
kfree_skb ( skb ) ;
continue ;
}
skb_pull ( skb , sizeof ( struct fcoe_hdr ) ) ;
fr_len = skb - > len - sizeof ( struct fcoe_crc_eof ) ;
2009-04-01 02:51:50 +04:00
stats - > RxFrames + + ;
stats - > RxWords + = fr_len / FCOE_WORD_TO_BYTE ;
2008-12-10 02:10:24 +03:00
fp = ( struct fc_frame * ) skb ;
fc_frame_init ( fp ) ;
fr_dev ( fp ) = lp ;
fr_sof ( fp ) = hp - > fcoe_sof ;
/* Copy out the CRC and EOF trailer for access */
if ( skb_copy_bits ( skb , fr_len , & crc_eof , sizeof ( crc_eof ) ) ) {
kfree_skb ( skb ) ;
continue ;
}
fr_eof ( fp ) = crc_eof . fcoe_eof ;
fr_crc ( fp ) = crc_eof . fcoe_crc32 ;
if ( pskb_trim ( skb , fr_len ) ) {
kfree_skb ( skb ) ;
continue ;
}
/*
* We only check CRC if no offload is available and if it is
* it ' s solicited data , in which case , the FCP layer would
* check it during the copy .
*/
2009-02-28 01:07:31 +03:00
if ( lp - > crc_offload & & skb - > ip_summed = = CHECKSUM_UNNECESSARY )
2008-12-10 02:10:24 +03:00
fr_flags ( fp ) & = ~ FCPHF_CRC_UNCHECKED ;
else
fr_flags ( fp ) | = FCPHF_CRC_UNCHECKED ;
fh = fc_frame_header_get ( fp ) ;
if ( fh - > fh_r_ctl = = FC_RCTL_DD_SOL_DATA & &
fh - > fh_type = = FC_TYPE_FCP ) {
fc_exch_recv ( lp , lp - > emp , fp ) ;
continue ;
}
if ( fr_flags ( fp ) & FCPHF_CRC_UNCHECKED ) {
if ( le32_to_cpu ( fr_crc ( fp ) ) ! =
~ crc32 ( ~ 0 , skb - > data , fr_len ) ) {
if ( debug_fcoe | | stats - > InvalidCRCCount < 5 )
printk ( KERN_WARNING " fcoe: dropping "
" frame with CRC error \n " ) ;
stats - > InvalidCRCCount + + ;
stats - > ErrorFrames + + ;
fc_frame_free ( fp ) ;
continue ;
}
fr_flags ( fp ) & = ~ FCPHF_CRC_UNCHECKED ;
}
/* non flogi and non data exchanges are handled here */
if ( unlikely ( fc - > flogi_progress ) )
fcoe_recv_flogi ( fc , fp , mac ) ;
fc_exch_recv ( lp , lp - > emp , fp ) ;
}
return 0 ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_recv_flogi ( ) - flogi receive function
2008-12-10 02:10:24 +03:00
* @ fc : associated fcoe_softc
* @ fp : the recieved frame
* @ sa : the source address of this flogi
*
* This is responsible to parse the flogi response and sets the corresponding
* mac address for the initiator , eitehr OUI based or GW based .
*
* Returns : none
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static void fcoe_recv_flogi ( struct fcoe_softc * fc , struct fc_frame * fp , u8 * sa )
{
struct fc_frame_header * fh ;
u8 op ;
fh = fc_frame_header_get ( fp ) ;
if ( fh - > fh_type ! = FC_TYPE_ELS )
return ;
op = fc_frame_payload_op ( fp ) ;
if ( op = = ELS_LS_ACC & & fh - > fh_r_ctl = = FC_RCTL_ELS_REP & &
fc - > flogi_oxid = = ntohs ( fh - > fh_ox_id ) ) {
/*
* FLOGI accepted .
* If the src mac addr is FC_OUI - based , then we mark the
* address_mode flag to use FC_OUI - based Ethernet DA .
* Otherwise we use the FCoE gateway addr
*/
if ( ! compare_ether_addr ( sa , ( u8 [ 6 ] ) FC_FCOE_FLOGI_MAC ) ) {
fc - > address_mode = FCOE_FCOUI_ADDR_MODE ;
} else {
memcpy ( fc - > dest_addr , sa , ETH_ALEN ) ;
fc - > address_mode = FCOE_GW_ADDR_MODE ;
}
/*
* Remove any previously - set unicast MAC filter .
* Add secondary FCoE MAC address filter for our OUI .
*/
rtnl_lock ( ) ;
if ( compare_ether_addr ( fc - > data_src_addr , ( u8 [ 6 ] ) { 0 } ) )
dev_unicast_delete ( fc - > real_dev , fc - > data_src_addr ,
ETH_ALEN ) ;
fc_fcoe_set_mac ( fc - > data_src_addr , fh - > fh_d_id ) ;
dev_unicast_add ( fc - > real_dev , fc - > data_src_addr , ETH_ALEN ) ;
rtnl_unlock ( ) ;
fc - > flogi_progress = 0 ;
} else if ( op = = ELS_FLOGI & & fh - > fh_r_ctl = = FC_RCTL_ELS_REQ & & sa ) {
/*
* Save source MAC for point - to - point responses .
*/
memcpy ( fc - > dest_addr , sa , ETH_ALEN ) ;
fc - > address_mode = FCOE_GW_ADDR_MODE ;
}
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_watchdog ( ) - fcoe timer callback
2008-12-10 02:10:24 +03:00
* @ vp :
*
2009-02-27 21:54:57 +03:00
* This checks the pending queue length for fcoe and set lport qfull
2008-12-10 02:10:24 +03:00
* if the FCOE_MAX_QUEUE_DEPTH is reached . This is done for all fc_lport on the
* fcoe_hostlist .
*
* Returns : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
void fcoe_watchdog ( ulong vp )
{
struct fcoe_softc * fc ;
read_lock ( & fcoe_hostlist_lock ) ;
list_for_each_entry ( fc , & fcoe_hostlist , list ) {
2009-02-27 21:56:27 +03:00
if ( fc - > lp )
fcoe_check_wait_queue ( fc - > lp ) ;
2008-12-10 02:10:24 +03:00
}
read_unlock ( & fcoe_hostlist_lock ) ;
fcoe_timer . expires = jiffies + ( 1 * HZ ) ;
add_timer ( & fcoe_timer ) ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_check_wait_queue ( ) - put the skb into fcoe pending xmit queue
2008-12-10 02:10:24 +03:00
* @ lp : the fc_port for this skb
* @ skb : the associated skb to be xmitted
*
* This empties the wait_queue , dequeue the head of the wait_queue queue
* and calls fcoe_start_io ( ) for each packet , if all skb have been
2009-02-27 21:56:27 +03:00
* transmitted , return qlen or - 1 if a error occurs , then restore
* wait_queue and try again later .
2008-12-10 02:10:24 +03:00
*
* The wait_queue is used when the skb transmit fails . skb will go
* in the wait_queue which will be emptied by the time function OR
* by the next skb transmit .
*
* Returns : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static int fcoe_check_wait_queue ( struct fc_lport * lp )
{
2009-02-27 21:56:32 +03:00
struct fcoe_softc * fc = lport_priv ( lp ) ;
2008-12-10 02:10:24 +03:00
struct sk_buff * skb ;
2009-02-27 21:56:27 +03:00
int rc = - 1 ;
2008-12-10 02:10:24 +03:00
spin_lock_bh ( & fc - > fcoe_pending_queue . lock ) ;
2009-02-27 21:56:27 +03:00
if ( fc - > fcoe_pending_queue_active )
goto out ;
fc - > fcoe_pending_queue_active = 1 ;
2009-02-27 21:56:32 +03:00
while ( fc - > fcoe_pending_queue . qlen ) {
/* keep qlen > 0 until fcoe_start_io succeeds */
fc - > fcoe_pending_queue . qlen + + ;
skb = __skb_dequeue ( & fc - > fcoe_pending_queue ) ;
spin_unlock_bh ( & fc - > fcoe_pending_queue . lock ) ;
rc = fcoe_start_io ( skb ) ;
spin_lock_bh ( & fc - > fcoe_pending_queue . lock ) ;
if ( rc ) {
__skb_queue_head ( & fc - > fcoe_pending_queue , skb ) ;
/* undo temporary increment above */
fc - > fcoe_pending_queue . qlen - - ;
break ;
2008-12-10 02:10:24 +03:00
}
2009-02-27 21:56:32 +03:00
/* undo temporary increment above */
fc - > fcoe_pending_queue . qlen - - ;
2008-12-10 02:10:24 +03:00
}
2009-02-27 21:56:32 +03:00
if ( fc - > fcoe_pending_queue . qlen < FCOE_LOW_QUEUE_DEPTH )
lp - > qfull = 0 ;
2009-02-27 21:56:27 +03:00
fc - > fcoe_pending_queue_active = 0 ;
rc = fc - > fcoe_pending_queue . qlen ;
out :
2008-12-10 02:10:24 +03:00
spin_unlock_bh ( & fc - > fcoe_pending_queue . lock ) ;
2009-02-27 21:56:27 +03:00
return rc ;
2008-12-10 02:10:24 +03:00
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_dev_setup ( ) - setup link change notification interface
*/
static void fcoe_dev_setup ( )
2008-12-10 02:10:24 +03:00
{
/*
* here setup a interface specific wd time to
* monitor the link state
*/
register_netdevice_notifier ( & fcoe_notifier ) ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_dev_setup ( ) - cleanup link change notification interface
*/
2008-12-10 02:10:24 +03:00
static void fcoe_dev_cleanup ( void )
{
unregister_netdevice_notifier ( & fcoe_notifier ) ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_device_notification ( ) - netdev event notification callback
2008-12-10 02:10:24 +03:00
* @ notifier : context of the notification
* @ event : type of event
* @ ptr : fixed array for output parsed ifname
*
* This function is called by the ethernet driver in case of link change event
*
* Returns : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static int fcoe_device_notification ( struct notifier_block * notifier ,
ulong event , void * ptr )
{
struct fc_lport * lp = NULL ;
struct net_device * real_dev = ptr ;
struct fcoe_softc * fc ;
struct fcoe_dev_stats * stats ;
2009-02-27 21:54:57 +03:00
u32 new_link_up ;
2008-12-10 02:10:24 +03:00
u32 mfs ;
int rc = NOTIFY_OK ;
read_lock ( & fcoe_hostlist_lock ) ;
list_for_each_entry ( fc , & fcoe_hostlist , list ) {
if ( fc - > real_dev = = real_dev ) {
lp = fc - > lp ;
break ;
}
}
read_unlock ( & fcoe_hostlist_lock ) ;
if ( lp = = NULL ) {
rc = NOTIFY_DONE ;
goto out ;
}
2009-02-27 21:54:57 +03:00
new_link_up = lp - > link_up ;
2008-12-10 02:10:24 +03:00
switch ( event ) {
case NETDEV_DOWN :
case NETDEV_GOING_DOWN :
2009-02-27 21:54:57 +03:00
new_link_up = 0 ;
2008-12-10 02:10:24 +03:00
break ;
case NETDEV_UP :
case NETDEV_CHANGE :
2009-02-27 21:54:57 +03:00
new_link_up = ! fcoe_link_ok ( lp ) ;
2008-12-10 02:10:24 +03:00
break ;
case NETDEV_CHANGEMTU :
mfs = fc - > real_dev - > mtu -
( sizeof ( struct fcoe_hdr ) +
sizeof ( struct fcoe_crc_eof ) ) ;
if ( mfs > = FC_MIN_MAX_FRAME )
fc_set_mfs ( lp , mfs ) ;
2009-02-27 21:54:57 +03:00
new_link_up = ! fcoe_link_ok ( lp ) ;
2008-12-10 02:10:24 +03:00
break ;
case NETDEV_REGISTER :
break ;
default :
FC_DBG ( " unknown event %ld call " , event ) ;
}
2009-02-27 21:54:57 +03:00
if ( lp - > link_up ! = new_link_up ) {
if ( new_link_up )
2008-12-10 02:10:24 +03:00
fc_linkup ( lp ) ;
else {
2009-04-01 02:51:50 +04:00
stats = fc_lport_get_stats ( lp ) ;
stats - > LinkFailureCount + + ;
2008-12-10 02:10:24 +03:00
fc_linkdown ( lp ) ;
fcoe_clean_pending_queue ( lp ) ;
}
}
out :
return rc ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_if_to_netdev ( ) - parse a name buffer to get netdev
2008-12-10 02:10:24 +03:00
* @ ifname : fixed array for output parsed ifname
* @ buffer : incoming buffer to be copied
*
* Returns : NULL or ptr to netdeive
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static struct net_device * fcoe_if_to_netdev ( const char * buffer )
{
char * cp ;
char ifname [ IFNAMSIZ + 2 ] ;
if ( buffer ) {
strlcpy ( ifname , buffer , IFNAMSIZ ) ;
cp = ifname + strlen ( ifname ) ;
while ( - - cp > = ifname & & * cp = = ' \n ' )
* cp = ' \0 ' ;
return dev_get_by_name ( & init_net , ifname ) ;
}
return NULL ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_netdev_to_module_owner ( ) - finds out the nic drive moddule of the netdev
2008-12-10 02:10:24 +03:00
* @ netdev : the target netdev
*
* Returns : ptr to the struct module , NULL for failure
2009-02-27 21:55:45 +03:00
*/
2009-02-27 21:55:50 +03:00
static struct module *
fcoe_netdev_to_module_owner ( const struct net_device * netdev )
2008-12-10 02:10:24 +03:00
{
struct device * dev ;
if ( ! netdev )
return NULL ;
dev = netdev - > dev . parent ;
if ( ! dev )
return NULL ;
if ( ! dev - > driver )
return NULL ;
return dev - > driver - > owner ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_ethdrv_get ( ) - Hold the Ethernet driver
2008-12-10 02:10:24 +03:00
* @ netdev : the target netdev
*
2009-02-27 21:55:45 +03:00
* Holds the Ethernet driver module by try_module_get ( ) for
* the corresponding netdev .
*
2008-12-10 02:10:24 +03:00
* Returns : 0 for succsss
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static int fcoe_ethdrv_get ( const struct net_device * netdev )
{
struct module * owner ;
owner = fcoe_netdev_to_module_owner ( netdev ) ;
if ( owner ) {
2008-12-30 00:45:41 +03:00
printk ( KERN_DEBUG " fcoe:hold driver module %s for %s \n " ,
module_name ( owner ) , netdev - > name ) ;
2008-12-10 02:10:24 +03:00
return try_module_get ( owner ) ;
}
return - ENODEV ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_ethdrv_put ( ) - Release the Ethernet driver
2008-12-10 02:10:24 +03:00
* @ netdev : the target netdev
*
2009-02-27 21:55:45 +03:00
* Releases the Ethernet driver module by module_put for
* the corresponding netdev .
*
2008-12-10 02:10:24 +03:00
* Returns : 0 for succsss
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static int fcoe_ethdrv_put ( const struct net_device * netdev )
{
struct module * owner ;
owner = fcoe_netdev_to_module_owner ( netdev ) ;
if ( owner ) {
2008-12-30 00:45:41 +03:00
printk ( KERN_DEBUG " fcoe:release driver module %s for %s \n " ,
module_name ( owner ) , netdev - > name ) ;
2008-12-10 02:10:24 +03:00
module_put ( owner ) ;
return 0 ;
}
return - ENODEV ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_destroy ( ) - handles the destroy from sysfs
2008-12-10 02:10:24 +03:00
* @ buffer : expcted to be a eth if name
* @ kp : associated kernel param
*
* Returns : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static int fcoe_destroy ( const char * buffer , struct kernel_param * kp )
{
int rc ;
struct net_device * netdev ;
netdev = fcoe_if_to_netdev ( buffer ) ;
if ( ! netdev ) {
rc = - ENODEV ;
goto out_nodev ;
}
/* look for existing lport */
if ( ! fcoe_hostlist_lookup ( netdev ) ) {
rc = - ENODEV ;
goto out_putdev ;
}
2009-03-27 19:06:31 +03:00
rc = fcoe_if_destroy ( netdev ) ;
2008-12-10 02:10:24 +03:00
if ( rc ) {
2009-03-27 19:06:31 +03:00
printk ( KERN_ERR " fcoe: fcoe_if_destroy(%s) failed \n " ,
2008-12-10 02:10:24 +03:00
netdev - > name ) ;
rc = - EIO ;
goto out_putdev ;
}
fcoe_ethdrv_put ( netdev ) ;
rc = 0 ;
out_putdev :
dev_put ( netdev ) ;
out_nodev :
return rc ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_create ( ) - Handles the create call from sysfs
2008-12-10 02:10:24 +03:00
* @ buffer : expcted to be a eth if name
* @ kp : associated kernel param
*
* Returns : 0 for success
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static int fcoe_create ( const char * buffer , struct kernel_param * kp )
{
int rc ;
struct net_device * netdev ;
netdev = fcoe_if_to_netdev ( buffer ) ;
if ( ! netdev ) {
rc = - ENODEV ;
goto out_nodev ;
}
/* look for existing lport */
if ( fcoe_hostlist_lookup ( netdev ) ) {
rc = - EEXIST ;
goto out_putdev ;
}
fcoe_ethdrv_get ( netdev ) ;
2009-03-27 19:06:31 +03:00
rc = fcoe_if_create ( netdev ) ;
2008-12-10 02:10:24 +03:00
if ( rc ) {
2009-03-27 19:06:31 +03:00
printk ( KERN_ERR " fcoe: fcoe_if_create(%s) failed \n " ,
2008-12-10 02:10:24 +03:00
netdev - > name ) ;
fcoe_ethdrv_put ( netdev ) ;
rc = - EIO ;
goto out_putdev ;
}
rc = 0 ;
out_putdev :
dev_put ( netdev ) ;
out_nodev :
return rc ;
}
module_param_call ( create , fcoe_create , NULL , NULL , S_IWUSR ) ;
__MODULE_PARM_TYPE ( create , " string " ) ;
MODULE_PARM_DESC ( create , " Create fcoe port using net device passed in. " ) ;
module_param_call ( destroy , fcoe_destroy , NULL , NULL , S_IWUSR ) ;
__MODULE_PARM_TYPE ( destroy , " string " ) ;
MODULE_PARM_DESC ( destroy , " Destroy fcoe port " ) ;
2009-02-27 21:55:45 +03:00
/**
* fcoe_link_ok ( ) - Check if link is ok for the fc_lport
2008-12-10 02:10:24 +03:00
* @ lp : ptr to the fc_lport
*
* Any permanently - disqualifying conditions have been previously checked .
* This also updates the speed setting , which may change with link for 100 / 1000.
*
* This function should probably be checking for PAUSE support at some point
* in the future . Currently Per - priority - pause is not determinable using
* ethtool , so we shouldn ' t be restrictive until that problem is resolved .
*
* Returns : 0 if link is OK for use by FCoE .
*
*/
int fcoe_link_ok ( struct fc_lport * lp )
{
2009-02-27 21:55:55 +03:00
struct fcoe_softc * fc = lport_priv ( lp ) ;
2008-12-10 02:10:24 +03:00
struct net_device * dev = fc - > real_dev ;
struct ethtool_cmd ecmd = { ETHTOOL_GSET } ;
int rc = 0 ;
if ( ( dev - > flags & IFF_UP ) & & netif_carrier_ok ( dev ) ) {
dev = fc - > phys_dev ;
if ( dev - > ethtool_ops - > get_settings ) {
dev - > ethtool_ops - > get_settings ( dev , & ecmd ) ;
lp - > link_supported_speeds & =
~ ( FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT ) ;
if ( ecmd . supported & ( SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full ) )
lp - > link_supported_speeds | = FC_PORTSPEED_1GBIT ;
if ( ecmd . supported & SUPPORTED_10000baseT_Full )
lp - > link_supported_speeds | =
FC_PORTSPEED_10GBIT ;
if ( ecmd . speed = = SPEED_1000 )
lp - > link_speed = FC_PORTSPEED_1GBIT ;
if ( ecmd . speed = = SPEED_10000 )
lp - > link_speed = FC_PORTSPEED_10GBIT ;
}
} else
rc = - 1 ;
return rc ;
}
EXPORT_SYMBOL_GPL ( fcoe_link_ok ) ;
2009-02-27 21:55:45 +03:00
/**
* fcoe_percpu_clean ( ) - Clear the pending skbs for an lport
2008-12-10 02:10:24 +03:00
* @ lp : the fc_lport
*/
void fcoe_percpu_clean ( struct fc_lport * lp )
{
struct fcoe_percpu_s * pp ;
struct fcoe_rcv_info * fr ;
struct sk_buff_head * list ;
struct sk_buff * skb , * next ;
struct sk_buff * head ;
2009-03-17 21:41:35 +03:00
unsigned int cpu ;
2008-12-10 02:10:24 +03:00
2009-03-17 21:41:35 +03:00
for_each_possible_cpu ( cpu ) {
pp = & per_cpu ( fcoe_percpu , cpu ) ;
spin_lock_bh ( & pp - > fcoe_rx_list . lock ) ;
list = & pp - > fcoe_rx_list ;
head = list - > next ;
for ( skb = head ; skb ! = ( struct sk_buff * ) list ;
skb = next ) {
next = skb - > next ;
fr = fcoe_dev_from_skb ( skb ) ;
if ( fr - > fr_dev = = lp ) {
__skb_unlink ( skb , list ) ;
kfree_skb ( skb ) ;
2008-12-10 02:10:24 +03:00
}
}
2009-03-17 21:41:35 +03:00
spin_unlock_bh ( & pp - > fcoe_rx_list . lock ) ;
2008-12-10 02:10:24 +03:00
}
}
EXPORT_SYMBOL_GPL ( fcoe_percpu_clean ) ;
/**
2009-02-27 21:55:45 +03:00
* fcoe_clean_pending_queue ( ) - Dequeue a skb and free it
2008-12-10 02:10:24 +03:00
* @ lp : the corresponding fc_lport
*
* Returns : none
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
void fcoe_clean_pending_queue ( struct fc_lport * lp )
{
struct fcoe_softc * fc = lport_priv ( lp ) ;
struct sk_buff * skb ;
spin_lock_bh ( & fc - > fcoe_pending_queue . lock ) ;
while ( ( skb = __skb_dequeue ( & fc - > fcoe_pending_queue ) ) ! = NULL ) {
spin_unlock_bh ( & fc - > fcoe_pending_queue . lock ) ;
kfree_skb ( skb ) ;
spin_lock_bh ( & fc - > fcoe_pending_queue . lock ) ;
}
spin_unlock_bh ( & fc - > fcoe_pending_queue . lock ) ;
}
EXPORT_SYMBOL_GPL ( fcoe_clean_pending_queue ) ;
/**
2009-02-27 21:55:45 +03:00
* libfc_host_alloc ( ) - Allocate a Scsi_Host with room for the fc_lport
2008-12-10 02:10:24 +03:00
* @ sht : ptr to the scsi host templ
* @ priv_size : size of private data after fc_lport
*
* Returns : ptr to Scsi_Host
2009-02-27 21:55:45 +03:00
* TODO : to libfc ?
2008-12-10 02:10:24 +03:00
*/
2009-02-27 21:55:50 +03:00
static inline struct Scsi_Host *
libfc_host_alloc ( struct scsi_host_template * sht , int priv_size )
2008-12-10 02:10:24 +03:00
{
return scsi_host_alloc ( sht , sizeof ( struct fc_lport ) + priv_size ) ;
}
/**
2009-02-27 21:55:45 +03:00
* fcoe_host_alloc ( ) - Allocate a Scsi_Host with room for the fcoe_softc
2008-12-10 02:10:24 +03:00
* @ sht : ptr to the scsi host templ
* @ priv_size : size of private data after fc_lport
*
* Returns : ptr to Scsi_Host
*/
struct Scsi_Host * fcoe_host_alloc ( struct scsi_host_template * sht , int priv_size )
{
return libfc_host_alloc ( sht , sizeof ( struct fcoe_softc ) + priv_size ) ;
}
EXPORT_SYMBOL_GPL ( fcoe_host_alloc ) ;
2009-02-27 21:55:45 +03:00
/**
* fcoe_reset ( ) - Resets the fcoe
2008-12-10 02:10:24 +03:00
* @ shost : shost the reset is from
*
* Returns : always 0
*/
int fcoe_reset ( struct Scsi_Host * shost )
{
struct fc_lport * lport = shost_priv ( shost ) ;
fc_lport_reset ( lport ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( fcoe_reset ) ;
2009-02-27 21:55:45 +03:00
/**
* fcoe_hostlist_lookup_softc ( ) - find the corresponding lport by a given device
2008-12-10 02:10:24 +03:00
* @ device : this is currently ptr to net_device
*
* Returns : NULL or the located fcoe_softc
*/
2009-02-27 21:55:50 +03:00
static struct fcoe_softc *
fcoe_hostlist_lookup_softc ( const struct net_device * dev )
2008-12-10 02:10:24 +03:00
{
struct fcoe_softc * fc ;
read_lock ( & fcoe_hostlist_lock ) ;
list_for_each_entry ( fc , & fcoe_hostlist , list ) {
if ( fc - > real_dev = = dev ) {
read_unlock ( & fcoe_hostlist_lock ) ;
return fc ;
}
}
read_unlock ( & fcoe_hostlist_lock ) ;
return NULL ;
}
2009-02-27 21:55:45 +03:00
/**
* fcoe_hostlist_lookup ( ) - Find the corresponding lport by netdev
2008-12-10 02:10:24 +03:00
* @ netdev : ptr to net_device
*
* Returns : 0 for success
*/
struct fc_lport * fcoe_hostlist_lookup ( const struct net_device * netdev )
{
struct fcoe_softc * fc ;
fc = fcoe_hostlist_lookup_softc ( netdev ) ;
return ( fc ) ? fc - > lp : NULL ;
}
EXPORT_SYMBOL_GPL ( fcoe_hostlist_lookup ) ;
2009-02-27 21:55:45 +03:00
/**
* fcoe_hostlist_add ( ) - Add a lport to lports list
2008-12-10 02:10:24 +03:00
* @ lp : ptr to the fc_lport to badded
*
* Returns : 0 for success
*/
int fcoe_hostlist_add ( const struct fc_lport * lp )
{
struct fcoe_softc * fc ;
fc = fcoe_hostlist_lookup_softc ( fcoe_netdev ( lp ) ) ;
if ( ! fc ) {
2009-02-27 21:55:55 +03:00
fc = lport_priv ( lp ) ;
2008-12-10 02:10:24 +03:00
write_lock_bh ( & fcoe_hostlist_lock ) ;
list_add_tail ( & fc - > list , & fcoe_hostlist ) ;
write_unlock_bh ( & fcoe_hostlist_lock ) ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( fcoe_hostlist_add ) ;
2009-02-27 21:55:45 +03:00
/**
* fcoe_hostlist_remove ( ) - remove a lport from lports list
2008-12-10 02:10:24 +03:00
* @ lp : ptr to the fc_lport to badded
*
* Returns : 0 for success
*/
int fcoe_hostlist_remove ( const struct fc_lport * lp )
{
struct fcoe_softc * fc ;
fc = fcoe_hostlist_lookup_softc ( fcoe_netdev ( lp ) ) ;
BUG_ON ( ! fc ) ;
write_lock_bh ( & fcoe_hostlist_lock ) ;
list_del ( & fc - > list ) ;
write_unlock_bh ( & fcoe_hostlist_lock ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( fcoe_hostlist_remove ) ;
/**
2009-02-27 21:55:45 +03:00
* fcoe_init ( ) - fcoe module loading initialization
2008-12-10 02:10:24 +03:00
*
* Returns 0 on success , negative on failure
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static int __init fcoe_init ( void )
{
2009-03-17 21:41:30 +03:00
unsigned int cpu ;
2009-03-17 21:41:46 +03:00
int rc = 0 ;
2008-12-10 02:10:24 +03:00
struct fcoe_percpu_s * p ;
INIT_LIST_HEAD ( & fcoe_hostlist ) ;
rwlock_init ( & fcoe_hostlist_lock ) ;
2009-03-17 21:41:30 +03:00
for_each_possible_cpu ( cpu ) {
2009-03-17 21:41:35 +03:00
p = & per_cpu ( fcoe_percpu , cpu ) ;
2009-03-17 21:41:30 +03:00
skb_queue_head_init ( & p - > fcoe_rx_list ) ;
}
2009-03-17 21:41:46 +03:00
for_each_online_cpu ( cpu )
fcoe_percpu_thread_create ( cpu ) ;
2008-12-10 02:10:24 +03:00
2009-03-17 21:41:46 +03:00
/* Initialize per CPU interrupt thread */
rc = register_hotcpu_notifier ( & fcoe_cpu_notifier ) ;
if ( rc )
goto out_free ;
2008-12-10 02:10:24 +03:00
2009-03-17 21:41:46 +03:00
/* Setup link change notification */
2008-12-10 02:10:24 +03:00
fcoe_dev_setup ( ) ;
2009-02-27 21:56:00 +03:00
setup_timer ( & fcoe_timer , fcoe_watchdog , 0 ) ;
mod_timer ( & fcoe_timer , jiffies + ( 10 * HZ ) ) ;
2008-12-10 02:10:24 +03:00
2009-03-27 19:06:31 +03:00
fcoe_if_init ( ) ;
2008-12-10 02:10:24 +03:00
return 0 ;
2009-03-17 21:41:46 +03:00
out_free :
for_each_online_cpu ( cpu ) {
fcoe_percpu_thread_destroy ( cpu ) ;
}
return rc ;
2008-12-10 02:10:24 +03:00
}
module_init ( fcoe_init ) ;
/**
2009-02-27 21:55:45 +03:00
* fcoe_exit ( ) - fcoe module unloading cleanup
2008-12-10 02:10:24 +03:00
*
* Returns 0 on success , negative on failure
2009-02-27 21:55:45 +03:00
*/
2008-12-10 02:10:24 +03:00
static void __exit fcoe_exit ( void )
{
2009-03-17 21:41:35 +03:00
unsigned int cpu ;
2008-12-10 02:10:24 +03:00
struct fcoe_softc * fc , * tmp ;
fcoe_dev_cleanup ( ) ;
2009-04-01 02:51:50 +04:00
/* Stop the timer */
2008-12-10 02:10:24 +03:00
del_timer_sync ( & fcoe_timer ) ;
2009-03-27 19:03:29 +03:00
/* releases the associated fcoe hosts */
2008-12-10 02:10:24 +03:00
list_for_each_entry_safe ( fc , tmp , & fcoe_hostlist , list )
2009-03-27 19:06:31 +03:00
fcoe_if_destroy ( fc - > real_dev ) ;
2008-12-10 02:10:24 +03:00
2009-03-17 21:41:46 +03:00
unregister_hotcpu_notifier ( & fcoe_cpu_notifier ) ;
for_each_online_cpu ( cpu ) {
fcoe_percpu_thread_destroy ( cpu ) ;
2008-12-10 02:10:24 +03:00
}
2009-03-27 19:06:31 +03:00
/* detach from scsi transport */
fcoe_if_exit ( ) ;
2008-12-10 02:10:24 +03:00
}
module_exit ( fcoe_exit ) ;