@ -2456,6 +2456,250 @@ static int ixgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
return ret ;
}
static int ixgbe_update_ethtool_fdir_entry ( struct ixgbe_adapter * adapter ,
struct ixgbe_fdir_filter * input ,
u16 sw_idx )
{
struct ixgbe_hw * hw = & adapter - > hw ;
struct hlist_node * node , * node2 , * parent ;
struct ixgbe_fdir_filter * rule ;
int err = - EINVAL ;
parent = NULL ;
rule = NULL ;
hlist_for_each_entry_safe ( rule , node , node2 ,
& adapter - > fdir_filter_list , fdir_node ) {
/* hash found, or no matching entry */
if ( rule - > sw_idx > = sw_idx )
break ;
parent = node ;
}
/* if there is an old rule occupying our place remove it */
if ( rule & & ( rule - > sw_idx = = sw_idx ) ) {
if ( ! input | | ( rule - > filter . formatted . bkt_hash ! =
input - > filter . formatted . bkt_hash ) ) {
err = ixgbe_fdir_erase_perfect_filter_82599 ( hw ,
& rule - > filter ,
sw_idx ) ;
}
hlist_del ( & rule - > fdir_node ) ;
kfree ( rule ) ;
adapter - > fdir_filter_count - - ;
}
/*
* If no input this was a delete , err should be 0 if a rule was
* successfully found and removed from the list else - EINVAL
*/
if ( ! input )
return err ;
/* initialize node and set software index */
INIT_HLIST_NODE ( & input - > fdir_node ) ;
/* add filter to the list */
if ( parent )
hlist_add_after ( parent , & input - > fdir_node ) ;
else
hlist_add_head ( & input - > fdir_node ,
& adapter - > fdir_filter_list ) ;
/* update counts */
adapter - > fdir_filter_count + + ;
return 0 ;
}
static int ixgbe_flowspec_to_flow_type ( struct ethtool_rx_flow_spec * fsp ,
u8 * flow_type )
{
switch ( fsp - > flow_type & ~ FLOW_EXT ) {
case TCP_V4_FLOW :
* flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4 ;
break ;
case UDP_V4_FLOW :
* flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4 ;
break ;
case SCTP_V4_FLOW :
* flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4 ;
break ;
case IP_USER_FLOW :
switch ( fsp - > h_u . usr_ip4_spec . proto ) {
case IPPROTO_TCP :
* flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4 ;
break ;
case IPPROTO_UDP :
* flow_type = IXGBE_ATR_FLOW_TYPE_UDPV4 ;
break ;
case IPPROTO_SCTP :
* flow_type = IXGBE_ATR_FLOW_TYPE_SCTPV4 ;
break ;
case 0 :
if ( ! fsp - > m_u . usr_ip4_spec . proto ) {
* flow_type = IXGBE_ATR_FLOW_TYPE_IPV4 ;
break ;
}
default :
return 0 ;
}
break ;
default :
return 0 ;
}
return 1 ;
}
static int ixgbe_add_ethtool_fdir_entry ( struct ixgbe_adapter * adapter ,
struct ethtool_rxnfc * cmd )
{
struct ethtool_rx_flow_spec * fsp =
( struct ethtool_rx_flow_spec * ) & cmd - > fs ;
struct ixgbe_hw * hw = & adapter - > hw ;
struct ixgbe_fdir_filter * input ;
union ixgbe_atr_input mask ;
int err ;
if ( ! ( adapter - > flags & IXGBE_FLAG_FDIR_PERFECT_CAPABLE ) )
return - EOPNOTSUPP ;
/*
* Don ' t allow programming if the action is a queue greater than
* the number of online Rx queues .
*/
if ( ( fsp - > ring_cookie ! = RX_CLS_FLOW_DISC ) & &
( fsp - > ring_cookie > = adapter - > num_rx_queues ) )
return - EINVAL ;
/* Don't allow indexes to exist outside of available space */
if ( fsp - > location > = ( ( 1024 < < adapter - > fdir_pballoc ) - 2 ) ) {
e_err ( drv , " Location out of range \n " ) ;
return - EINVAL ;
}
input = kzalloc ( sizeof ( * input ) , GFP_ATOMIC ) ;
if ( ! input )
return - ENOMEM ;
memset ( & mask , 0 , sizeof ( union ixgbe_atr_input ) ) ;
/* set SW index */
input - > sw_idx = fsp - > location ;
/* record flow type */
if ( ! ixgbe_flowspec_to_flow_type ( fsp ,
& input - > filter . formatted . flow_type ) ) {
e_err ( drv , " Unrecognized flow type \n " ) ;
goto err_out ;
}
mask . formatted . flow_type = IXGBE_ATR_L4TYPE_IPV6_MASK |
IXGBE_ATR_L4TYPE_MASK ;
if ( input - > filter . formatted . flow_type = = IXGBE_ATR_FLOW_TYPE_IPV4 )
mask . formatted . flow_type & = IXGBE_ATR_L4TYPE_IPV6_MASK ;
/* Copy input into formatted structures */
input - > filter . formatted . src_ip [ 0 ] = fsp - > h_u . tcp_ip4_spec . ip4src ;
mask . formatted . src_ip [ 0 ] = fsp - > m_u . tcp_ip4_spec . ip4src ;
input - > filter . formatted . dst_ip [ 0 ] = fsp - > h_u . tcp_ip4_spec . ip4dst ;
mask . formatted . dst_ip [ 0 ] = fsp - > m_u . tcp_ip4_spec . ip4dst ;
input - > filter . formatted . src_port = fsp - > h_u . tcp_ip4_spec . psrc ;
mask . formatted . src_port = fsp - > m_u . tcp_ip4_spec . psrc ;
input - > filter . formatted . dst_port = fsp - > h_u . tcp_ip4_spec . pdst ;
mask . formatted . dst_port = fsp - > m_u . tcp_ip4_spec . pdst ;
if ( fsp - > flow_type & FLOW_EXT ) {
input - > filter . formatted . vm_pool =
( unsigned char ) ntohl ( fsp - > h_ext . data [ 1 ] ) ;
mask . formatted . vm_pool =
( unsigned char ) ntohl ( fsp - > m_ext . data [ 1 ] ) ;
input - > filter . formatted . vlan_id = fsp - > h_ext . vlan_tci ;
mask . formatted . vlan_id = fsp - > m_ext . vlan_tci ;
input - > filter . formatted . flex_bytes =
fsp - > h_ext . vlan_etype ;
mask . formatted . flex_bytes = fsp - > m_ext . vlan_etype ;
}
/* determine if we need to drop or route the packet */
if ( fsp - > ring_cookie = = RX_CLS_FLOW_DISC )
input - > action = IXGBE_FDIR_DROP_QUEUE ;
else
input - > action = fsp - > ring_cookie ;
spin_lock ( & adapter - > fdir_perfect_lock ) ;
if ( hlist_empty ( & adapter - > fdir_filter_list ) ) {
/* save mask and program input mask into HW */
memcpy ( & adapter - > fdir_mask , & mask , sizeof ( mask ) ) ;
err = ixgbe_fdir_set_input_mask_82599 ( hw , & mask ) ;
if ( err ) {
e_err ( drv , " Error writing mask \n " ) ;
goto err_out_w_lock ;
}
} else if ( memcmp ( & adapter - > fdir_mask , & mask , sizeof ( mask ) ) ) {
e_err ( drv , " Only one mask supported per port \n " ) ;
goto err_out_w_lock ;
}
/* apply mask and compute/store hash */
ixgbe_atr_compute_perfect_hash_82599 ( & input - > filter , & mask ) ;
/* program filters to filter memory */
err = ixgbe_fdir_write_perfect_filter_82599 ( hw ,
& input - > filter , input - > sw_idx ,
adapter - > rx_ring [ input - > action ] - > reg_idx ) ;
if ( err )
goto err_out_w_lock ;
ixgbe_update_ethtool_fdir_entry ( adapter , input , input - > sw_idx ) ;
spin_unlock ( & adapter - > fdir_perfect_lock ) ;
return err ;
err_out_w_lock :
spin_unlock ( & adapter - > fdir_perfect_lock ) ;
err_out :
kfree ( input ) ;
return - EINVAL ;
}
static int ixgbe_del_ethtool_fdir_entry ( struct ixgbe_adapter * adapter ,
struct ethtool_rxnfc * cmd )
{
struct ethtool_rx_flow_spec * fsp =
( struct ethtool_rx_flow_spec * ) & cmd - > fs ;
int err ;
spin_lock ( & adapter - > fdir_perfect_lock ) ;
err = ixgbe_update_ethtool_fdir_entry ( adapter , NULL , fsp - > location ) ;
spin_unlock ( & adapter - > fdir_perfect_lock ) ;
return err ;
}
static int ixgbe_set_rxnfc ( struct net_device * dev , struct ethtool_rxnfc * cmd )
{
struct ixgbe_adapter * adapter = netdev_priv ( dev ) ;
int ret = - EOPNOTSUPP ;
switch ( cmd - > cmd ) {
case ETHTOOL_SRXCLSRLINS :
ret = ixgbe_add_ethtool_fdir_entry ( adapter , cmd ) ;
break ;
case ETHTOOL_SRXCLSRLDEL :
ret = ixgbe_del_ethtool_fdir_entry ( adapter , cmd ) ;
break ;
default :
break ;
}
return ret ;
}
static const struct ethtool_ops ixgbe_ethtool_ops = {
. get_settings = ixgbe_get_settings ,
. set_settings = ixgbe_set_settings ,
@ -2492,6 +2736,7 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
. get_flags = ethtool_op_get_flags ,
. set_flags = ixgbe_set_flags ,
. get_rxnfc = ixgbe_get_rxnfc ,
. set_rxnfc = ixgbe_set_rxnfc ,
} ;
void ixgbe_set_ethtool_ops ( struct net_device * netdev )