2017-02-15 17:28:23 +03:00
/*
* QLogic FCoE Offload Driver
2017-05-31 16:33:49 +03:00
* Copyright ( c ) 2016 - 2017 Cavium Inc .
2017-02-15 17:28:23 +03:00
*
* This software is available 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 .
*/
# include <linux/if_ether.h>
# include <linux/if_vlan.h>
# include "qedf.h"
extern const struct qed_fcoe_ops * qed_ops ;
/*
* FIP VLAN functions that will eventually move to libfcoe .
*/
void qedf_fcoe_send_vlan_req ( struct qedf_ctx * qedf )
{
struct sk_buff * skb ;
char * eth_fr ;
int fr_len ;
struct fip_vlan * vlan ;
# define MY_FIP_ALL_FCF_MACS ((__u8[6]) { 1, 0x10, 0x18, 1, 0, 2 })
static u8 my_fcoe_all_fcfs [ ETH_ALEN ] = MY_FIP_ALL_FCF_MACS ;
skb = dev_alloc_skb ( sizeof ( struct fip_vlan ) ) ;
if ( ! skb )
return ;
fr_len = sizeof ( * vlan ) ;
eth_fr = ( char * ) skb - > data ;
vlan = ( struct fip_vlan * ) eth_fr ;
memset ( vlan , 0 , sizeof ( * vlan ) ) ;
ether_addr_copy ( vlan - > eth . h_source , qedf - > mac ) ;
ether_addr_copy ( vlan - > eth . h_dest , my_fcoe_all_fcfs ) ;
vlan - > eth . h_proto = htons ( ETH_P_FIP ) ;
vlan - > fip . fip_ver = FIP_VER_ENCAPS ( FIP_VER ) ;
vlan - > fip . fip_op = htons ( FIP_OP_VLAN ) ;
vlan - > fip . fip_subcode = FIP_SC_VL_REQ ;
vlan - > fip . fip_dl_len = htons ( sizeof ( vlan - > desc ) / FIP_BPW ) ;
vlan - > desc . mac . fd_desc . fip_dtype = FIP_DT_MAC ;
vlan - > desc . mac . fd_desc . fip_dlen = sizeof ( vlan - > desc . mac ) / FIP_BPW ;
ether_addr_copy ( vlan - > desc . mac . fd_mac , qedf - > mac ) ;
vlan - > desc . wwnn . fd_desc . fip_dtype = FIP_DT_NAME ;
vlan - > desc . wwnn . fd_desc . fip_dlen = sizeof ( vlan - > desc . wwnn ) / FIP_BPW ;
put_unaligned_be64 ( qedf - > lport - > wwnn , & vlan - > desc . wwnn . fd_wwn ) ;
skb_put ( skb , sizeof ( * vlan ) ) ;
skb - > protocol = htons ( ETH_P_FIP ) ;
skb_reset_mac_header ( skb ) ;
skb_reset_network_header ( skb ) ;
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_DISC , " Sending FIP VLAN "
" request. " ) ;
if ( atomic_read ( & qedf - > link_state ) ! = QEDF_LINK_UP ) {
QEDF_WARN ( & ( qedf - > dbg_ctx ) , " Cannot send vlan request "
" because link is not up. \n " ) ;
kfree_skb ( skb ) ;
return ;
}
qed_ops - > ll2 - > start_xmit ( qedf - > cdev , skb ) ;
}
static void qedf_fcoe_process_vlan_resp ( struct qedf_ctx * qedf ,
struct sk_buff * skb )
{
struct fip_header * fiph ;
struct fip_desc * desc ;
u16 vid = 0 ;
ssize_t rlen ;
size_t dlen ;
fiph = ( struct fip_header * ) ( ( ( void * ) skb - > data ) + 2 * ETH_ALEN + 2 ) ;
rlen = ntohs ( fiph - > fip_dl_len ) * 4 ;
desc = ( struct fip_desc * ) ( fiph + 1 ) ;
while ( rlen > 0 ) {
dlen = desc - > fip_dlen * FIP_BPW ;
switch ( desc - > fip_dtype ) {
case FIP_DT_VLAN :
vid = ntohs ( ( ( struct fip_vlan_desc * ) desc ) - > fd_vlan ) ;
break ;
}
desc = ( struct fip_desc * ) ( ( char * ) desc + dlen ) ;
rlen - = dlen ;
}
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_DISC , " VLAN response, "
" vid=0x%x. \n " , vid ) ;
if ( vid > 0 & & qedf - > vlan_id ! = vid ) {
qedf_set_vlan_id ( qedf , vid ) ;
/* Inform waiter that it's ok to call fcoe_ctlr_link up() */
2017-03-23 16:58:47 +03:00
if ( ! completion_done ( & qedf - > fipvlan_compl ) )
complete ( & qedf - > fipvlan_compl ) ;
2017-02-15 17:28:23 +03:00
}
}
void qedf_fip_send ( struct fcoe_ctlr * fip , struct sk_buff * skb )
{
struct qedf_ctx * qedf = container_of ( fip , struct qedf_ctx , ctlr ) ;
struct ethhdr * eth_hdr ;
struct vlan_ethhdr * vlan_hdr ;
struct fip_header * fiph ;
u16 op , vlan_tci = 0 ;
u8 sub ;
if ( ! test_bit ( QEDF_LL2_STARTED , & qedf - > flags ) ) {
QEDF_WARN ( & ( qedf - > dbg_ctx ) , " LL2 not started \n " ) ;
kfree_skb ( skb ) ;
return ;
}
fiph = ( struct fip_header * ) ( ( void * ) skb - > data + 2 * ETH_ALEN + 2 ) ;
eth_hdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
op = ntohs ( fiph - > fip_op ) ;
sub = fiph - > fip_subcode ;
if ( ! qedf - > vlan_hw_insert ) {
vlan_hdr = ( struct vlan_ethhdr * ) skb_push ( skb , sizeof ( * vlan_hdr )
- sizeof ( * eth_hdr ) ) ;
memcpy ( vlan_hdr , eth_hdr , 2 * ETH_ALEN ) ;
vlan_hdr - > h_vlan_proto = htons ( ETH_P_8021Q ) ;
vlan_hdr - > h_vlan_encapsulated_proto = eth_hdr - > h_proto ;
vlan_hdr - > h_vlan_TCI = vlan_tci = htons ( qedf - > vlan_id ) ;
}
/* Update eth_hdr since we added a VLAN tag */
eth_hdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_LL2 , " FIP frame send: "
" dest=%pM op=%x sub=%x vlan=%04x. " , eth_hdr - > h_dest , op , sub ,
ntohs ( vlan_tci ) ) ;
if ( qedf_dump_frames )
print_hex_dump ( KERN_WARNING , " fip " , DUMP_PREFIX_OFFSET , 16 , 1 ,
skb - > data , skb - > len , false ) ;
qed_ops - > ll2 - > start_xmit ( qedf - > cdev , skb ) ;
}
/* Process incoming FIP frames. */
void qedf_fip_recv ( struct qedf_ctx * qedf , struct sk_buff * skb )
{
struct ethhdr * eth_hdr ;
struct fip_header * fiph ;
struct fip_desc * desc ;
struct fip_mac_desc * mp ;
struct fip_wwn_desc * wp ;
struct fip_vn_desc * vp ;
size_t rlen , dlen ;
uint32_t cvl_port_id ;
__u8 cvl_mac [ ETH_ALEN ] ;
u16 op ;
u8 sub ;
eth_hdr = ( struct ethhdr * ) skb_mac_header ( skb ) ;
fiph = ( struct fip_header * ) ( ( void * ) skb - > data + 2 * ETH_ALEN + 2 ) ;
op = ntohs ( fiph - > fip_op ) ;
sub = fiph - > fip_subcode ;
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_LL2 , " FIP frame received: "
" skb=%p fiph=%p source=%pM op=%x sub=%x " , skb , fiph ,
eth_hdr - > h_source , op , sub ) ;
if ( qedf_dump_frames )
print_hex_dump ( KERN_WARNING , " fip " , DUMP_PREFIX_OFFSET , 16 , 1 ,
skb - > data , skb - > len , false ) ;
/* Handle FIP VLAN resp in the driver */
if ( op = = FIP_OP_VLAN & & sub = = FIP_SC_VL_NOTE ) {
qedf_fcoe_process_vlan_resp ( qedf , skb ) ;
qedf - > vlan_hw_insert = 0 ;
kfree_skb ( skb ) ;
} else if ( op = = FIP_OP_CTRL & & sub = = FIP_SC_CLR_VLINK ) {
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_DISC , " Clear virtual "
" link received. \n " ) ;
/* Check that an FCF has been selected by fcoe */
if ( qedf - > ctlr . sel_fcf = = NULL ) {
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_DISC ,
" Dropping CVL since FCF has not been selected "
" yet. " ) ;
return ;
}
cvl_port_id = 0 ;
memset ( cvl_mac , 0 , ETH_ALEN ) ;
/*
* We need to loop through the CVL descriptors to determine
* if we want to reset the fcoe link
*/
rlen = ntohs ( fiph - > fip_dl_len ) * FIP_BPW ;
desc = ( struct fip_desc * ) ( fiph + 1 ) ;
while ( rlen > = sizeof ( * desc ) ) {
dlen = desc - > fip_dlen * FIP_BPW ;
switch ( desc - > fip_dtype ) {
case FIP_DT_MAC :
mp = ( struct fip_mac_desc * ) desc ;
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_LL2 ,
2017-03-04 11:07:04 +03:00
" fd_mac=%pM \n " , mp - > fd_mac ) ;
2017-02-15 17:28:23 +03:00
ether_addr_copy ( cvl_mac , mp - > fd_mac ) ;
break ;
case FIP_DT_NAME :
wp = ( struct fip_wwn_desc * ) desc ;
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_LL2 ,
" fc_wwpn=%016llx. \n " ,
get_unaligned_be64 ( & wp - > fd_wwn ) ) ;
break ;
case FIP_DT_VN_ID :
vp = ( struct fip_vn_desc * ) desc ;
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_LL2 ,
" fd_fc_id=%x. \n " , ntoh24 ( vp - > fd_fc_id ) ) ;
cvl_port_id = ntoh24 ( vp - > fd_fc_id ) ;
break ;
default :
/* Ignore anything else */
break ;
}
desc = ( struct fip_desc * ) ( ( char * ) desc + dlen ) ;
rlen - = dlen ;
}
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_LL2 ,
" cvl_port_id=%06x cvl_mac=%pM. \n " , cvl_port_id ,
cvl_mac ) ;
if ( cvl_port_id = = qedf - > lport - > port_id & &
ether_addr_equal ( cvl_mac ,
qedf - > ctlr . sel_fcf - > fcf_mac ) ) {
fcoe_ctlr_link_down ( & qedf - > ctlr ) ;
qedf_wait_for_upload ( qedf ) ;
fcoe_ctlr_link_up ( & qedf - > ctlr ) ;
}
kfree_skb ( skb ) ;
} else {
/* Everything else is handled by libfcoe */
__skb_pull ( skb , ETH_HLEN ) ;
fcoe_ctlr_recv ( & qedf - > ctlr , skb ) ;
}
}
void qedf_update_src_mac ( struct fc_lport * lport , u8 * addr )
{
struct qedf_ctx * qedf = lport_priv ( lport ) ;
QEDF_INFO ( & ( qedf - > dbg_ctx ) , QEDF_LOG_DISC ,
" Setting data_src_addr=%pM. \n " , addr ) ;
ether_addr_copy ( qedf - > data_src_addr , addr ) ;
}
u8 * qedf_get_src_mac ( struct fc_lport * lport )
{
u8 mac [ ETH_ALEN ] ;
u8 port_id [ 3 ] ;
struct qedf_ctx * qedf = lport_priv ( lport ) ;
/* We need to use the lport port_id to create the data_src_addr */
if ( is_zero_ether_addr ( qedf - > data_src_addr ) ) {
hton24 ( port_id , lport - > port_id ) ;
fc_fcoe_set_mac ( mac , port_id ) ;
qedf - > ctlr . update_mac ( lport , mac ) ;
}
return qedf - > data_src_addr ;
}