2013-09-10 03:38:16 +00:00
/*
* Copyright ( c ) 2013 , Cisco Systems , Inc . All rights reserved .
*
2015-09-30 13:34:00 -07:00
* 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
* 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 .
2013-09-10 03:38:16 +00:00
*
* 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/netdevice.h>
# include <linux/pci.h>
# include "enic_api.h"
# include "usnic_common_pkt_hdr.h"
# include "usnic_fwd.h"
# include "usnic_log.h"
2014-01-09 14:48:07 -08:00
static int usnic_fwd_devcmd_locked ( struct usnic_fwd_dev * ufdev , int vnic_idx ,
enum vnic_devcmd_cmd cmd , u64 * a0 ,
u64 * a1 )
{
int status ;
struct net_device * netdev = ufdev - > netdev ;
lockdep_assert_held ( & ufdev - > lock ) ;
status = enic_api_devcmd_proxy_by_index ( netdev ,
vnic_idx ,
cmd ,
a0 , a1 ,
1000 ) ;
if ( status ) {
if ( status = = ERR_EINVAL & & cmd = = CMD_DEL_FILTER ) {
usnic_dbg ( " Dev %s vnic idx %u cmd %u already deleted " ,
ufdev - > name , vnic_idx , cmd ) ;
} else {
usnic_err ( " Dev %s vnic idx %u cmd %u failed with status %d \n " ,
ufdev - > name , vnic_idx , cmd ,
status ) ;
}
} else {
usnic_dbg ( " Dev %s vnic idx %u cmd %u success " ,
ufdev - > name , vnic_idx , cmd ) ;
}
return status ;
}
static int usnic_fwd_devcmd ( struct usnic_fwd_dev * ufdev , int vnic_idx ,
enum vnic_devcmd_cmd cmd , u64 * a0 , u64 * a1 )
{
int status ;
spin_lock ( & ufdev - > lock ) ;
status = usnic_fwd_devcmd_locked ( ufdev , vnic_idx , cmd , a0 , a1 ) ;
spin_unlock ( & ufdev - > lock ) ;
return status ;
}
2013-09-10 03:38:16 +00:00
struct usnic_fwd_dev * usnic_fwd_dev_alloc ( struct pci_dev * pdev )
{
struct usnic_fwd_dev * ufdev ;
ufdev = kzalloc ( sizeof ( * ufdev ) , GFP_KERNEL ) ;
if ( ! ufdev )
return NULL ;
ufdev - > pdev = pdev ;
ufdev - > netdev = pci_get_drvdata ( pdev ) ;
spin_lock_init ( & ufdev - > lock ) ;
2018-07-23 15:37:01 -07:00
BUILD_BUG_ON ( sizeof ( ufdev - > name ) ! = sizeof ( ufdev - > netdev - > name ) ) ;
strcpy ( ufdev - > name , ufdev - > netdev - > name ) ;
2013-09-10 03:38:16 +00:00
return ufdev ;
}
void usnic_fwd_dev_free ( struct usnic_fwd_dev * ufdev )
{
kfree ( ufdev ) ;
}
2014-01-09 14:48:07 -08:00
void usnic_fwd_set_mac ( struct usnic_fwd_dev * ufdev , char mac [ ETH_ALEN ] )
2013-09-10 03:38:16 +00:00
{
2014-01-09 14:48:07 -08:00
spin_lock ( & ufdev - > lock ) ;
memcpy ( & ufdev - > mac , mac , sizeof ( ufdev - > mac ) ) ;
spin_unlock ( & ufdev - > lock ) ;
}
2013-09-10 03:38:16 +00:00
2017-08-17 15:50:41 +03:00
void usnic_fwd_add_ipaddr ( struct usnic_fwd_dev * ufdev , __be32 inaddr )
2014-01-09 14:48:15 -08:00
{
spin_lock ( & ufdev - > lock ) ;
2017-08-17 15:50:41 +03:00
if ( ! ufdev - > inaddr )
2014-01-09 14:48:15 -08:00
ufdev - > inaddr = inaddr ;
spin_unlock ( & ufdev - > lock ) ;
}
void usnic_fwd_del_ipaddr ( struct usnic_fwd_dev * ufdev )
{
spin_lock ( & ufdev - > lock ) ;
ufdev - > inaddr = 0 ;
spin_unlock ( & ufdev - > lock ) ;
}
2014-01-09 14:48:07 -08:00
void usnic_fwd_carrier_up ( struct usnic_fwd_dev * ufdev )
{
2013-09-10 03:38:16 +00:00
spin_lock ( & ufdev - > lock ) ;
2014-01-09 14:48:07 -08:00
ufdev - > link_up = 1 ;
2013-09-10 03:38:16 +00:00
spin_unlock ( & ufdev - > lock ) ;
2014-01-09 14:48:07 -08:00
}
2013-09-10 03:38:16 +00:00
2014-01-09 14:48:07 -08:00
void usnic_fwd_carrier_down ( struct usnic_fwd_dev * ufdev )
{
spin_lock ( & ufdev - > lock ) ;
ufdev - > link_up = 0 ;
spin_unlock ( & ufdev - > lock ) ;
}
void usnic_fwd_set_mtu ( struct usnic_fwd_dev * ufdev , unsigned int mtu )
{
spin_lock ( & ufdev - > lock ) ;
ufdev - > mtu = mtu ;
spin_unlock ( & ufdev - > lock ) ;
}
static int usnic_fwd_dev_ready_locked ( struct usnic_fwd_dev * ufdev )
{
lockdep_assert_held ( & ufdev - > lock ) ;
if ( ! ufdev - > link_up )
return - EPERM ;
return 0 ;
}
2014-01-09 14:48:15 -08:00
static int validate_filter_locked ( struct usnic_fwd_dev * ufdev ,
struct filter * filter )
{
lockdep_assert_held ( & ufdev - > lock ) ;
if ( filter - > type = = FILTER_IPV4_5TUPLE ) {
if ( ! ( filter - > u . ipv4 . flags & FILTER_FIELD_5TUP_DST_AD ) )
return - EACCES ;
if ( ! ( filter - > u . ipv4 . flags & FILTER_FIELD_5TUP_DST_PT ) )
return - EBUSY ;
else if ( ufdev - > inaddr = = 0 )
return - EINVAL ;
else if ( filter - > u . ipv4 . dst_port = = 0 )
return - ERANGE ;
else if ( ntohl ( ufdev - > inaddr ) ! = filter - > u . ipv4 . dst_addr )
return - EFAULT ;
else
return 0 ;
}
return 0 ;
}
2014-01-09 14:48:07 -08:00
static void fill_tlv ( struct filter_tlv * tlv , struct filter * filter ,
struct filter_action * action )
{
tlv - > type = CLSF_TLV_FILTER ;
tlv - > length = sizeof ( struct filter ) ;
* ( ( struct filter * ) & tlv - > val ) = * filter ;
tlv = ( struct filter_tlv * ) ( ( char * ) tlv + sizeof ( struct filter_tlv ) +
sizeof ( struct filter ) ) ;
tlv - > type = CLSF_TLV_ACTION ;
tlv - > length = sizeof ( struct filter_action ) ;
* ( ( struct filter_action * ) & tlv - > val ) = * action ;
2013-09-10 03:38:16 +00:00
}
2014-01-09 14:48:07 -08:00
struct usnic_fwd_flow *
usnic_fwd_alloc_flow ( struct usnic_fwd_dev * ufdev , struct filter * filter ,
struct usnic_filter_action * uaction )
2013-09-10 03:38:16 +00:00
{
2014-01-09 14:48:07 -08:00
struct filter_tlv * tlv ;
2013-09-10 03:38:16 +00:00
struct pci_dev * pdev ;
2014-01-09 14:48:07 -08:00
struct usnic_fwd_flow * flow ;
uint64_t a0 , a1 ;
uint64_t tlv_size ;
2013-09-10 03:38:16 +00:00
dma_addr_t tlv_pa ;
2014-01-09 14:48:07 -08:00
int status ;
2013-09-10 03:38:16 +00:00
pdev = ufdev - > pdev ;
2014-01-09 14:48:07 -08:00
tlv_size = ( 2 * sizeof ( struct filter_tlv ) + sizeof ( struct filter ) +
sizeof ( struct filter_action ) ) ;
flow = kzalloc ( sizeof ( * flow ) , GFP_ATOMIC ) ;
if ( ! flow )
return ERR_PTR ( - ENOMEM ) ;
2013-09-10 03:38:16 +00:00
tlv = pci_alloc_consistent ( pdev , tlv_size , & tlv_pa ) ;
if ( ! tlv ) {
usnic_err ( " Failed to allocate memory \n " ) ;
2014-01-09 14:48:07 -08:00
status = - ENOMEM ;
goto out_free_flow ;
2013-09-10 03:38:16 +00:00
}
2014-01-09 14:48:07 -08:00
fill_tlv ( tlv , filter , & uaction - > action ) ;
spin_lock ( & ufdev - > lock ) ;
status = usnic_fwd_dev_ready_locked ( ufdev ) ;
if ( status ) {
usnic_err ( " Forwarding dev %s not ready with status %d \n " ,
ufdev - > name , status ) ;
goto out_free_tlv ;
2013-09-10 03:38:16 +00:00
}
2014-01-09 14:48:15 -08:00
status = validate_filter_locked ( ufdev , filter ) ;
if ( status ) {
usnic_err ( " Failed to validate filter with status %d \n " ,
status ) ;
goto out_free_tlv ;
}
2014-01-09 14:48:07 -08:00
/* Issue Devcmd */
2013-09-10 03:38:16 +00:00
a0 = tlv_pa ;
a1 = tlv_size ;
2014-01-09 14:48:07 -08:00
status = usnic_fwd_devcmd_locked ( ufdev , uaction - > vnic_idx ,
CMD_ADD_FILTER , & a0 , & a1 ) ;
2013-09-10 03:38:16 +00:00
if ( status ) {
usnic_err ( " VF %s Filter add failed with status:%d " ,
2014-01-09 14:48:07 -08:00
ufdev - > name , status ) ;
status = - EFAULT ;
goto out_free_tlv ;
2013-09-10 03:38:16 +00:00
} else {
2014-01-09 14:48:07 -08:00
usnic_dbg ( " VF %s FILTER ID:%llu " , ufdev - > name , a0 ) ;
2013-09-10 03:38:16 +00:00
}
2014-01-09 14:48:07 -08:00
flow - > flow_id = ( uint32_t ) a0 ;
flow - > vnic_idx = uaction - > vnic_idx ;
flow - > ufdev = ufdev ;
2013-09-10 03:38:16 +00:00
2014-01-09 14:48:07 -08:00
out_free_tlv :
spin_unlock ( & ufdev - > lock ) ;
pci_free_consistent ( pdev , tlv_size , tlv , tlv_pa ) ;
if ( ! status )
return flow ;
out_free_flow :
kfree ( flow ) ;
return ERR_PTR ( status ) ;
2013-09-10 03:38:16 +00:00
}
2014-01-09 14:48:07 -08:00
int usnic_fwd_dealloc_flow ( struct usnic_fwd_flow * flow )
2013-09-10 03:38:16 +00:00
{
int status ;
u64 a0 , a1 ;
2014-01-09 14:48:07 -08:00
a0 = flow - > flow_id ;
2013-09-10 03:38:16 +00:00
2014-01-09 14:48:07 -08:00
status = usnic_fwd_devcmd ( flow - > ufdev , flow - > vnic_idx ,
2013-09-10 03:38:16 +00:00
CMD_DEL_FILTER , & a0 , & a1 ) ;
if ( status ) {
if ( status = = ERR_EINVAL ) {
usnic_dbg ( " Filter %u already deleted for VF Idx %u pf: %s status: %d " ,
2014-01-09 14:48:07 -08:00
flow - > flow_id , flow - > vnic_idx ,
flow - > ufdev - > name , status ) ;
2013-09-10 03:38:16 +00:00
} else {
usnic_err ( " PF %s VF Idx %u Filter: %u FILTER DELETE failed with status %d " ,
2014-01-09 14:48:07 -08:00
flow - > ufdev - > name , flow - > vnic_idx ,
flow - > flow_id , status ) ;
2013-09-10 03:38:16 +00:00
}
2014-01-09 14:48:07 -08:00
status = 0 ;
/*
* Log the error and fake success to the caller because if
* a flow fails to be deleted in the firmware , it is an
* unrecoverable error .
*/
2013-09-10 03:38:16 +00:00
} else {
usnic_dbg ( " PF %s VF Idx %u Filter: %u FILTER DELETED " ,
2014-01-09 14:48:07 -08:00
flow - > ufdev - > name , flow - > vnic_idx ,
flow - > flow_id ) ;
2013-09-10 03:38:16 +00:00
}
2014-01-09 14:48:07 -08:00
kfree ( flow ) ;
2013-09-10 03:38:16 +00:00
return status ;
}
2014-01-09 14:48:07 -08:00
int usnic_fwd_enable_qp ( struct usnic_fwd_dev * ufdev , int vnic_idx , int qp_idx )
2013-09-10 03:38:16 +00:00
{
int status ;
struct net_device * pf_netdev ;
u64 a0 , a1 ;
pf_netdev = ufdev - > netdev ;
2014-01-09 14:48:07 -08:00
a0 = qp_idx ;
2013-09-10 03:38:16 +00:00
a1 = CMD_QP_RQWQ ;
2014-01-09 14:48:07 -08:00
status = usnic_fwd_devcmd ( ufdev , vnic_idx , CMD_QP_ENABLE ,
& a0 , & a1 ) ;
2013-09-10 03:38:16 +00:00
if ( status ) {
usnic_err ( " PF %s VNIC Index %u RQ Index: %u ENABLE Failed with status %d " ,
netdev_name ( pf_netdev ) ,
vnic_idx ,
2014-01-09 14:48:07 -08:00
qp_idx ,
2013-09-10 03:38:16 +00:00
status ) ;
} else {
usnic_dbg ( " PF %s VNIC Index %u RQ Index: %u ENABLED " ,
netdev_name ( pf_netdev ) ,
2014-01-09 14:48:07 -08:00
vnic_idx , qp_idx ) ;
2013-09-10 03:38:16 +00:00
}
return status ;
}
2014-01-09 14:48:07 -08:00
int usnic_fwd_disable_qp ( struct usnic_fwd_dev * ufdev , int vnic_idx , int qp_idx )
2013-09-10 03:38:16 +00:00
{
int status ;
u64 a0 , a1 ;
struct net_device * pf_netdev ;
pf_netdev = ufdev - > netdev ;
2014-01-09 14:48:07 -08:00
a0 = qp_idx ;
2013-09-10 03:38:16 +00:00
a1 = CMD_QP_RQWQ ;
2014-01-09 14:48:07 -08:00
status = usnic_fwd_devcmd ( ufdev , vnic_idx , CMD_QP_DISABLE ,
& a0 , & a1 ) ;
2013-09-10 03:38:16 +00:00
if ( status ) {
usnic_err ( " PF %s VNIC Index %u RQ Index: %u DISABLE Failed with status %d " ,
netdev_name ( pf_netdev ) ,
vnic_idx ,
2014-01-09 14:48:07 -08:00
qp_idx ,
2013-09-10 03:38:16 +00:00
status ) ;
} else {
usnic_dbg ( " PF %s VNIC Index %u RQ Index: %u DISABLED " ,
netdev_name ( pf_netdev ) ,
vnic_idx ,
2014-01-09 14:48:07 -08:00
qp_idx ) ;
2013-09-10 03:38:16 +00:00
}
return status ;
}