2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2008-04-27 12:55:59 +01:00
/****************************************************************************
2013-08-29 23:32:48 +01:00
* Driver for Solarflare network controllers and boards
2008-04-27 12:55:59 +01:00
* Copyright 2005 - 2006 Fen Systems Ltd .
2013-08-29 23:32:48 +01:00
* Copyright 2006 - 2013 Solarflare Communications Inc .
2008-04-27 12:55:59 +01:00
*/
# include <linux/netdevice.h>
# include <linux/ethtool.h>
# include <linux/rtnetlink.h>
2010-12-07 19:11:26 +00:00
# include <linux/in.h>
2008-04-27 12:55:59 +01:00
# include "net_driver.h"
2008-12-12 21:50:46 -08:00
# include "workarounds.h"
2008-05-07 13:36:19 +01:00
# include "selftest.h"
2008-04-27 12:55:59 +01:00
# include "efx.h"
2020-01-08 16:10:32 +00:00
# include "efx_channels.h"
# include "rx_common.h"
# include "tx_common.h"
2020-01-10 13:27:32 +00:00
# include "ethtool_common.h"
2010-09-20 08:43:42 +00:00
# include "filter.h"
2009-11-29 15:12:08 +00:00
# include "nic.h"
2008-04-27 12:55:59 +01:00
2008-09-01 12:47:16 +01:00
# define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
2008-04-27 12:55:59 +01:00
/**************************************************************************
*
* Ethtool operations
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/* Identify device by flashing LEDs */
2011-04-02 00:43:46 +01:00
static int efx_ethtool_phys_id ( struct net_device * net_dev ,
enum ethtool_phys_id_state state )
2008-04-27 12:55:59 +01:00
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-04-13 13:09:10 +00:00
enum efx_led_mode mode = EFX_LED_DEFAULT ;
2008-04-27 12:55:59 +01:00
2011-04-02 00:43:46 +01:00
switch ( state ) {
case ETHTOOL_ID_ON :
mode = EFX_LED_ON ;
break ;
case ETHTOOL_ID_OFF :
mode = EFX_LED_OFF ;
break ;
case ETHTOOL_ID_INACTIVE :
mode = EFX_LED_DEFAULT ;
break ;
2011-04-13 13:09:10 +00:00
case ETHTOOL_ID_ACTIVE :
return 1 ; /* cycle on/off once per second */
2011-04-02 00:43:46 +01:00
}
2009-11-23 16:03:45 +00:00
2011-04-02 00:43:46 +01:00
efx - > type - > set_id_led ( efx , mode ) ;
2008-04-27 12:55:59 +01:00
return 0 ;
}
/* This must be called with rtnl_lock held. */
2016-12-15 00:12:53 +01:00
static int
efx_ethtool_get_link_ksettings ( struct net_device * net_dev ,
struct ethtool_link_ksettings * cmd )
2008-04-27 12:55:59 +01:00
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2009-11-29 03:42:41 +00:00
struct efx_link_state * link_state = & efx - > link_state ;
2016-12-15 00:12:53 +01:00
u32 supported ;
2008-04-27 12:55:59 +01:00
mutex_lock ( & efx - > mac_lock ) ;
2016-12-15 00:12:53 +01:00
efx - > phy_op - > get_link_ksettings ( efx , cmd ) ;
2008-04-27 12:55:59 +01:00
mutex_unlock ( & efx - > mac_lock ) ;
2009-11-29 03:42:41 +00:00
/* Both MACs support pause frames (bidirectional and respond-only) */
2016-12-15 00:12:53 +01:00
ethtool_convert_link_mode_to_legacy_u32 ( & supported ,
cmd - > link_modes . supported ) ;
supported | = SUPPORTED_Pause | SUPPORTED_Asym_Pause ;
ethtool_convert_legacy_u32_to_link_mode ( cmd - > link_modes . supported ,
supported ) ;
2009-11-29 03:42:41 +00:00
if ( LOOPBACK_INTERNAL ( efx ) ) {
2016-12-15 00:12:53 +01:00
cmd - > base . speed = link_state - > speed ;
cmd - > base . duplex = link_state - > fd ? DUPLEX_FULL : DUPLEX_HALF ;
2009-11-29 03:42:41 +00:00
}
2008-12-12 21:50:08 -08:00
return 0 ;
2008-04-27 12:55:59 +01:00
}
/* This must be called with rtnl_lock held. */
2016-12-15 00:12:53 +01:00
static int
efx_ethtool_set_link_ksettings ( struct net_device * net_dev ,
const struct ethtool_link_ksettings * cmd )
2008-04-27 12:55:59 +01:00
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
int rc ;
2010-02-03 09:31:57 +00:00
/* GMAC does not support 1000Mbps HD */
2016-12-15 00:12:53 +01:00
if ( ( cmd - > base . speed = = SPEED_1000 ) & &
( cmd - > base . duplex ! = DUPLEX_FULL ) ) {
2010-06-23 11:30:07 +00:00
netif_dbg ( efx , drv , efx - > net_dev ,
" rejecting unsupported 1000Mbps HD setting \n " ) ;
2008-12-12 21:50:08 -08:00
return - EINVAL ;
}
2008-04-27 12:55:59 +01:00
mutex_lock ( & efx - > mac_lock ) ;
2016-12-15 00:12:53 +01:00
rc = efx - > phy_op - > set_link_ksettings ( efx , cmd ) ;
2008-04-27 12:55:59 +01:00
mutex_unlock ( & efx - > mac_lock ) ;
return rc ;
}
2010-06-21 03:06:53 +00:00
static int efx_ethtool_get_regs_len ( struct net_device * net_dev )
{
return efx_nic_get_regs_len ( netdev_priv ( net_dev ) ) ;
}
static void efx_ethtool_get_regs ( struct net_device * net_dev ,
struct ethtool_regs * regs , void * buf )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
regs - > version = efx - > type - > revision ;
efx_nic_get_regs ( efx , buf ) ;
}
2008-05-07 13:36:19 +01:00
static void efx_ethtool_self_test ( struct net_device * net_dev ,
struct ethtool_test * test , u64 * data )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-02-16 03:48:38 +00:00
struct efx_self_tests * efx_tests ;
2014-02-12 19:00:16 +00:00
bool already_up ;
2011-02-16 03:48:38 +00:00
int rc = - ENOMEM ;
efx_tests = kzalloc ( sizeof ( * efx_tests ) , GFP_KERNEL ) ;
if ( ! efx_tests )
goto fail ;
2012-07-27 19:31:16 +01:00
if ( efx - > state ! = STATE_READY ) {
2014-02-12 19:00:28 +00:00
rc = - EBUSY ;
2014-02-12 19:00:16 +00:00
goto out ;
2008-05-07 13:36:19 +01:00
}
2010-12-07 18:29:52 +00:00
netif_info ( efx , drv , efx - > net_dev , " starting %sline testing \n " ,
( test - > flags & ETH_TEST_FL_OFFLINE ) ? " off " : " on " ) ;
2008-05-07 13:36:19 +01:00
/* We need rx buffers and interrupts. */
already_up = ( efx - > net_dev - > flags & IFF_UP ) ;
if ( ! already_up ) {
2018-12-06 17:05:36 +00:00
rc = dev_open ( efx - > net_dev , NULL ) ;
2008-05-07 13:36:19 +01:00
if ( rc ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , drv , efx - > net_dev ,
" failed opening device. \n " ) ;
2014-02-12 19:00:16 +00:00
goto out ;
2008-05-07 13:36:19 +01:00
}
}
2011-02-16 03:48:38 +00:00
rc = efx_selftest ( efx , efx_tests , test - > flags ) ;
2008-05-07 13:36:19 +01:00
if ( ! already_up )
dev_close ( efx - > net_dev ) ;
2010-12-07 18:29:52 +00:00
netif_info ( efx , drv , efx - > net_dev , " %s %sline self-tests \n " ,
rc = = 0 ? " passed " : " failed " ,
( test - > flags & ETH_TEST_FL_OFFLINE ) ? " off " : " on " ) ;
2008-05-07 13:36:19 +01:00
2014-02-12 19:00:16 +00:00
out :
2011-02-16 03:48:38 +00:00
efx_ethtool_fill_self_tests ( efx , efx_tests , NULL , data ) ;
kfree ( efx_tests ) ;
fail :
2008-05-07 13:36:19 +01:00
if ( rc )
test - > flags | = ETH_TEST_FL_FAILED ;
}
2008-04-27 12:55:59 +01:00
/* Restart autonegotiation */
static int efx_ethtool_nway_reset ( struct net_device * net_dev )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
2009-04-29 08:05:08 +00:00
return mdio45_nway_restart ( & efx - > mdio ) ;
2008-04-27 12:55:59 +01:00
}
2011-09-05 07:42:25 +00:00
/*
* Each channel has a single IRQ and moderation timer , started by any
* completion ( or other event ) . Unless the module parameter
* separate_tx_channels is set , IRQs and moderation are therefore
* shared between RX and TX completions . In this case , when RX IRQ
* moderation is explicitly changed then TX IRQ moderation is
* automatically changed too , but otherwise we fail if the two values
* are requested to be different .
*
2011-09-05 07:43:49 +00:00
* The hardware does not support a limit on the number of completions
* before an IRQ , so we do not use the max_frames fields . We should
* report and require that max_frames = = ( usecs ! = 0 ) , but this would
* invalidate existing user documentation .
*
* The hardware does not have distinct settings for interrupt
* moderation while the previous IRQ is being handled , so we should
* not use the ' irq ' fields . However , an earlier developer
* misunderstood the meaning of the ' irq ' fields and the driver did
* not support the standard fields . To avoid invalidating existing
* user documentation , we report and accept changes through either the
* standard or ' irq ' fields . If both are changed at the same time , we
* prefer the standard field .
*
2011-09-05 07:42:25 +00:00
* We implement adaptive IRQ moderation , but use a different algorithm
* from that assumed in the definition of struct ethtool_coalesce .
* Therefore we do not use any of the adaptive moderation parameters
* in it .
*/
2008-04-27 12:55:59 +01:00
static int efx_ethtool_get_coalesce ( struct net_device * net_dev ,
struct ethtool_coalesce * coalesce )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-09-05 07:42:25 +00:00
unsigned int tx_usecs , rx_usecs ;
bool rx_adaptive ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:42:25 +00:00
efx_get_irq_moderation ( efx , & tx_usecs , & rx_usecs , & rx_adaptive ) ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:43:49 +00:00
coalesce - > tx_coalesce_usecs = tx_usecs ;
2011-09-05 07:42:25 +00:00
coalesce - > tx_coalesce_usecs_irq = tx_usecs ;
2011-09-05 07:43:49 +00:00
coalesce - > rx_coalesce_usecs = rx_usecs ;
2011-09-05 07:42:25 +00:00
coalesce - > rx_coalesce_usecs_irq = rx_usecs ;
coalesce - > use_adaptive_rx_coalesce = rx_adaptive ;
2009-10-23 08:32:13 +00:00
2008-04-27 12:55:59 +01:00
return 0 ;
}
static int efx_ethtool_set_coalesce ( struct net_device * net_dev ,
struct ethtool_coalesce * coalesce )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
struct efx_channel * channel ;
2011-09-05 07:41:44 +00:00
unsigned int tx_usecs , rx_usecs ;
2011-09-05 07:43:04 +00:00
bool adaptive , rx_may_override_tx ;
int rc ;
2008-04-27 12:55:59 +01:00
2009-03-20 13:30:37 +00:00
if ( coalesce - > use_adaptive_tx_coalesce )
2011-09-05 07:41:27 +00:00
return - EINVAL ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:42:25 +00:00
efx_get_irq_moderation ( efx , & tx_usecs , & rx_usecs , & adaptive ) ;
2011-09-05 07:43:49 +00:00
if ( coalesce - > rx_coalesce_usecs ! = rx_usecs )
rx_usecs = coalesce - > rx_coalesce_usecs ;
else
rx_usecs = coalesce - > rx_coalesce_usecs_irq ;
2009-03-20 13:30:37 +00:00
adaptive = coalesce - > use_adaptive_rx_coalesce ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:42:25 +00:00
/* If channels are shared, TX IRQ moderation can be quietly
* overridden unless it is changed from its old value .
*/
2011-09-05 07:43:49 +00:00
rx_may_override_tx = ( coalesce - > tx_coalesce_usecs = = tx_usecs & &
coalesce - > tx_coalesce_usecs_irq = = tx_usecs ) ;
if ( coalesce - > tx_coalesce_usecs ! = tx_usecs )
tx_usecs = coalesce - > tx_coalesce_usecs ;
else
tx_usecs = coalesce - > tx_coalesce_usecs_irq ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:43:04 +00:00
rc = efx_init_irq_moderation ( efx , tx_usecs , rx_usecs , adaptive ,
rx_may_override_tx ) ;
if ( rc ! = 0 )
return rc ;
2008-04-27 12:55:59 +01:00
efx_for_each_channel ( channel , efx )
2009-11-29 03:42:31 +00:00
efx - > type - > push_irq_moderation ( channel ) ;
2008-04-27 12:55:59 +01:00
return 0 ;
}
2010-09-10 06:42:33 +00:00
static void efx_ethtool_get_ringparam ( struct net_device * net_dev ,
struct ethtool_ringparam * ring )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
ring - > rx_max_pending = EFX_MAX_DMAQ_SIZE ;
2014-01-23 14:35:48 +00:00
ring - > tx_max_pending = EFX_TXQ_MAX_ENT ( efx ) ;
2010-09-10 06:42:33 +00:00
ring - > rx_pending = efx - > rxq_entries ;
ring - > tx_pending = efx - > txq_entries ;
}
static int efx_ethtool_set_ringparam ( struct net_device * net_dev ,
struct ethtool_ringparam * ring )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2012-07-30 15:57:44 +00:00
u32 txq_entries ;
2010-09-10 06:42:33 +00:00
if ( ring - > rx_mini_pending | | ring - > rx_jumbo_pending | |
ring - > rx_pending > EFX_MAX_DMAQ_SIZE | |
2014-01-23 14:35:48 +00:00
ring - > tx_pending > EFX_TXQ_MAX_ENT ( efx ) )
2010-09-10 06:42:33 +00:00
return - EINVAL ;
2012-07-30 15:57:44 +00:00
if ( ring - > rx_pending < EFX_RXQ_MIN_ENT ) {
2010-09-10 06:42:33 +00:00
netif_err ( efx , drv , efx - > net_dev ,
2012-07-30 15:57:44 +00:00
" RX queues cannot be smaller than %u \n " ,
EFX_RXQ_MIN_ENT ) ;
2010-09-10 06:42:33 +00:00
return - EINVAL ;
}
2012-07-30 15:57:44 +00:00
txq_entries = max ( ring - > tx_pending , EFX_TXQ_MIN_ENT ( efx ) ) ;
if ( txq_entries ! = ring - > tx_pending )
netif_warn ( efx , drv , efx - > net_dev ,
" increasing TX queue size to minimum of %u \n " ,
txq_entries ) ;
return efx_realloc_channels ( efx , ring - > rx_pending , txq_entries ) ;
2010-09-10 06:42:33 +00:00
}
2008-04-27 12:55:59 +01:00
static int efx_ethtool_set_pauseparam ( struct net_device * net_dev ,
struct ethtool_pauseparam * pause )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-05-17 17:53:22 -04:00
u8 wanted_fc , old_fc ;
2009-11-29 03:42:41 +00:00
u32 old_adv ;
int rc = 0 ;
mutex_lock ( & efx - > mac_lock ) ;
2008-04-27 12:55:59 +01:00
2008-12-12 21:50:46 -08:00
wanted_fc = ( ( pause - > rx_pause ? EFX_FC_RX : 0 ) |
( pause - > tx_pause ? EFX_FC_TX : 0 ) |
( pause - > autoneg ? EFX_FC_AUTO : 0 ) ) ;
if ( ( wanted_fc & EFX_FC_TX ) & & ! ( wanted_fc & EFX_FC_RX ) ) {
2010-06-23 11:30:07 +00:00
netif_dbg ( efx , drv , efx - > net_dev ,
" Flow control unsupported: tx ON rx OFF \n " ) ;
2009-11-29 03:42:41 +00:00
rc = - EINVAL ;
goto out ;
2008-12-12 21:50:46 -08:00
}
2018-01-10 18:00:14 +00:00
if ( ( wanted_fc & EFX_FC_AUTO ) & & ! efx - > link_advertising [ 0 ] ) {
2010-06-23 11:30:07 +00:00
netif_dbg ( efx , drv , efx - > net_dev ,
" Autonegotiation is disabled \n " ) ;
2009-11-29 03:42:41 +00:00
rc = - EINVAL ;
goto out ;
2008-12-12 21:50:46 -08:00
}
2012-09-13 01:11:25 +01:00
/* Hook for Falcon bug 11482 workaround */
if ( efx - > type - > prepare_enable_fc_tx & &
( wanted_fc & EFX_FC_TX ) & & ! ( efx - > wanted_fc & EFX_FC_TX ) )
efx - > type - > prepare_enable_fc_tx ( efx ) ;
2008-12-12 21:50:46 -08:00
2018-01-10 18:00:14 +00:00
old_adv = efx - > link_advertising [ 0 ] ;
2009-11-29 03:42:41 +00:00
old_fc = efx - > wanted_fc ;
efx_link_set_wanted_fc ( efx , wanted_fc ) ;
2018-01-10 18:00:14 +00:00
if ( efx - > link_advertising [ 0 ] ! = old_adv | |
2009-11-29 03:42:41 +00:00
( efx - > wanted_fc ^ old_fc ) & EFX_FC_AUTO ) {
rc = efx - > phy_op - > reconfigure ( efx ) ;
if ( rc ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , drv , efx - > net_dev ,
" Unable to advertise requested flow "
" control setting \n " ) ;
2009-11-29 03:42:41 +00:00
goto out ;
}
}
2008-12-12 21:50:46 -08:00
2009-11-29 03:42:41 +00:00
/* Reconfigure the MAC. The PHY *may* generate a link state change event
* if the user just changed the advertised capabilities , but there ' s no
* harm doing this twice */
2015-05-20 11:10:03 +01:00
efx_mac_reconfigure ( efx ) ;
2008-12-12 21:50:46 -08:00
2009-11-29 03:42:41 +00:00
out :
2008-12-12 21:50:46 -08:00
mutex_unlock ( & efx - > mac_lock ) ;
2008-04-27 12:55:59 +01:00
2009-11-29 03:42:41 +00:00
return rc ;
2008-04-27 12:55:59 +01:00
}
2009-11-29 03:43:07 +00:00
static void efx_ethtool_get_wol ( struct net_device * net_dev ,
struct ethtool_wolinfo * wol )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
return efx - > type - > get_wol ( efx , wol ) ;
}
static int efx_ethtool_set_wol ( struct net_device * net_dev ,
struct ethtool_wolinfo * wol )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
return efx - > type - > set_wol ( efx , wol - > wolopts ) ;
}
2010-10-18 05:27:31 +00:00
static int efx_ethtool_reset ( struct net_device * net_dev , u32 * flags )
2009-11-29 03:43:15 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-06-24 20:50:07 +01:00
int rc ;
2009-11-29 03:43:15 +00:00
2011-06-24 20:50:07 +01:00
rc = efx - > type - > map_reset_flags ( flags ) ;
if ( rc < 0 )
return rc ;
2009-11-29 03:43:15 +00:00
2011-06-24 20:50:07 +01:00
return efx_reset ( efx , rc ) ;
2009-11-29 03:43:15 +00:00
}
2012-10-27 00:33:28 +01:00
/* MAC address mask including only I/G bit */
2014-03-07 18:27:41 +00:00
static const u8 mac_addr_ig_mask [ ETH_ALEN ] __aligned ( 2 ) = { 0x01 , 0 , 0 , 0 , 0 , 0 } ;
2012-02-02 22:41:49 +00:00
2012-09-06 02:11:04 +01:00
# define IP4_ADDR_FULL_MASK ((__force __be32)~0)
2016-02-05 11:16:50 +00:00
# define IP_PROTO_FULL_MASK 0xFF
2012-09-06 02:11:04 +01:00
# define PORT_FULL_MASK ((__force __be16)~0)
2012-10-27 00:33:28 +01:00
# define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
2012-09-06 02:11:04 +01:00
2016-02-05 11:16:50 +00:00
static inline void ip6_fill_mask ( __be32 * mask )
{
mask [ 0 ] = mask [ 1 ] = mask [ 2 ] = mask [ 3 ] = ~ ( __be32 ) 0 ;
}
2012-01-03 12:05:47 +00:00
static int efx_ethtool_get_class_rule ( struct efx_nic * efx ,
2018-03-08 15:45:17 +00:00
struct ethtool_rx_flow_spec * rule ,
u32 * rss_context )
2012-01-03 12:05:47 +00:00
{
struct ethtool_tcpip4_spec * ip_entry = & rule - > h_u . tcp_ip4_spec ;
struct ethtool_tcpip4_spec * ip_mask = & rule - > m_u . tcp_ip4_spec ;
2016-02-05 11:16:50 +00:00
struct ethtool_usrip4_spec * uip_entry = & rule - > h_u . usr_ip4_spec ;
struct ethtool_usrip4_spec * uip_mask = & rule - > m_u . usr_ip4_spec ;
struct ethtool_tcpip6_spec * ip6_entry = & rule - > h_u . tcp_ip6_spec ;
struct ethtool_tcpip6_spec * ip6_mask = & rule - > m_u . tcp_ip6_spec ;
struct ethtool_usrip6_spec * uip6_entry = & rule - > h_u . usr_ip6_spec ;
struct ethtool_usrip6_spec * uip6_mask = & rule - > m_u . usr_ip6_spec ;
2012-02-02 22:41:49 +00:00
struct ethhdr * mac_entry = & rule - > h_u . ether_spec ;
struct ethhdr * mac_mask = & rule - > m_u . ether_spec ;
2012-01-03 12:05:47 +00:00
struct efx_filter_spec spec ;
int rc ;
rc = efx_filter_get_filter_safe ( efx , EFX_FILTER_PRI_MANUAL ,
rule - > location , & spec ) ;
if ( rc )
return rc ;
2012-10-30 01:01:52 +00:00
if ( spec . dmaq_id = = EFX_FILTER_RX_DMAQ_ID_DROP )
2012-01-03 12:05:47 +00:00
rule - > ring_cookie = RX_CLS_FLOW_DISC ;
else
rule - > ring_cookie = spec . dmaq_id ;
2012-10-27 00:33:28 +01:00
if ( ( spec . match_flags & EFX_FILTER_MATCH_ETHER_TYPE ) & &
spec . ether_type = = htons ( ETH_P_IP ) & &
( spec . match_flags & EFX_FILTER_MATCH_IP_PROTO ) & &
( spec . ip_proto = = IPPROTO_TCP | | spec . ip_proto = = IPPROTO_UDP ) & &
! ( spec . match_flags &
~ ( EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST |
EFX_FILTER_MATCH_IP_PROTO |
EFX_FILTER_MATCH_LOC_PORT | EFX_FILTER_MATCH_REM_PORT ) ) ) {
rule - > flow_type = ( ( spec . ip_proto = = IPPROTO_TCP ) ?
TCP_V4_FLOW : UDP_V4_FLOW ) ;
if ( spec . match_flags & EFX_FILTER_MATCH_LOC_HOST ) {
ip_entry - > ip4dst = spec . loc_host [ 0 ] ;
ip_mask - > ip4dst = IP4_ADDR_FULL_MASK ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_REM_HOST ) {
ip_entry - > ip4src = spec . rem_host [ 0 ] ;
ip_mask - > ip4src = IP4_ADDR_FULL_MASK ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_LOC_PORT ) {
ip_entry - > pdst = spec . loc_port ;
ip_mask - > pdst = PORT_FULL_MASK ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_REM_PORT ) {
ip_entry - > psrc = spec . rem_port ;
ip_mask - > psrc = PORT_FULL_MASK ;
}
2016-02-05 11:16:50 +00:00
} else if ( ( spec . match_flags & EFX_FILTER_MATCH_ETHER_TYPE ) & &
spec . ether_type = = htons ( ETH_P_IPV6 ) & &
( spec . match_flags & EFX_FILTER_MATCH_IP_PROTO ) & &
( spec . ip_proto = = IPPROTO_TCP | | spec . ip_proto = = IPPROTO_UDP ) & &
! ( spec . match_flags &
~ ( EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST |
EFX_FILTER_MATCH_IP_PROTO |
EFX_FILTER_MATCH_LOC_PORT | EFX_FILTER_MATCH_REM_PORT ) ) ) {
rule - > flow_type = ( ( spec . ip_proto = = IPPROTO_TCP ) ?
TCP_V6_FLOW : UDP_V6_FLOW ) ;
if ( spec . match_flags & EFX_FILTER_MATCH_LOC_HOST ) {
memcpy ( ip6_entry - > ip6dst , spec . loc_host ,
sizeof ( ip6_entry - > ip6dst ) ) ;
ip6_fill_mask ( ip6_mask - > ip6dst ) ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_REM_HOST ) {
memcpy ( ip6_entry - > ip6src , spec . rem_host ,
sizeof ( ip6_entry - > ip6src ) ) ;
ip6_fill_mask ( ip6_mask - > ip6src ) ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_LOC_PORT ) {
ip6_entry - > pdst = spec . loc_port ;
ip6_mask - > pdst = PORT_FULL_MASK ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_REM_PORT ) {
ip6_entry - > psrc = spec . rem_port ;
ip6_mask - > psrc = PORT_FULL_MASK ;
}
2012-10-27 00:33:28 +01:00
} else if ( ! ( spec . match_flags &
~ ( EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG |
EFX_FILTER_MATCH_REM_MAC | EFX_FILTER_MATCH_ETHER_TYPE |
EFX_FILTER_MATCH_OUTER_VID ) ) ) {
2012-01-03 12:05:47 +00:00
rule - > flow_type = ETHER_FLOW ;
2012-10-27 00:33:28 +01:00
if ( spec . match_flags &
( EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_MAC_IG ) ) {
2014-03-07 18:27:41 +00:00
ether_addr_copy ( mac_entry - > h_dest , spec . loc_mac ) ;
2012-10-27 00:33:28 +01:00
if ( spec . match_flags & EFX_FILTER_MATCH_LOC_MAC )
2014-03-07 18:27:41 +00:00
eth_broadcast_addr ( mac_mask - > h_dest ) ;
2012-10-27 00:33:28 +01:00
else
2014-03-07 18:27:41 +00:00
ether_addr_copy ( mac_mask - > h_dest ,
mac_addr_ig_mask ) ;
2012-01-03 12:05:47 +00:00
}
2012-10-27 00:33:28 +01:00
if ( spec . match_flags & EFX_FILTER_MATCH_REM_MAC ) {
2014-03-07 18:27:41 +00:00
ether_addr_copy ( mac_entry - > h_source , spec . rem_mac ) ;
eth_broadcast_addr ( mac_mask - > h_source ) ;
2012-10-27 00:33:28 +01:00
}
if ( spec . match_flags & EFX_FILTER_MATCH_ETHER_TYPE ) {
mac_entry - > h_proto = spec . ether_type ;
mac_mask - > h_proto = ETHER_TYPE_FULL_MASK ;
}
2016-02-05 11:16:50 +00:00
} else if ( spec . match_flags & EFX_FILTER_MATCH_ETHER_TYPE & &
spec . ether_type = = htons ( ETH_P_IP ) & &
! ( spec . match_flags &
~ ( EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST |
EFX_FILTER_MATCH_IP_PROTO ) ) ) {
rule - > flow_type = IPV4_USER_FLOW ;
uip_entry - > ip_ver = ETH_RX_NFC_IP4 ;
if ( spec . match_flags & EFX_FILTER_MATCH_IP_PROTO ) {
uip_mask - > proto = IP_PROTO_FULL_MASK ;
uip_entry - > proto = spec . ip_proto ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_LOC_HOST ) {
uip_entry - > ip4dst = spec . loc_host [ 0 ] ;
uip_mask - > ip4dst = IP4_ADDR_FULL_MASK ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_REM_HOST ) {
uip_entry - > ip4src = spec . rem_host [ 0 ] ;
uip_mask - > ip4src = IP4_ADDR_FULL_MASK ;
}
} else if ( spec . match_flags & EFX_FILTER_MATCH_ETHER_TYPE & &
spec . ether_type = = htons ( ETH_P_IPV6 ) & &
! ( spec . match_flags &
~ ( EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_OUTER_VID |
EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_REM_HOST |
EFX_FILTER_MATCH_IP_PROTO ) ) ) {
rule - > flow_type = IPV6_USER_FLOW ;
if ( spec . match_flags & EFX_FILTER_MATCH_IP_PROTO ) {
uip6_mask - > l4_proto = IP_PROTO_FULL_MASK ;
uip6_entry - > l4_proto = spec . ip_proto ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_LOC_HOST ) {
memcpy ( uip6_entry - > ip6dst , spec . loc_host ,
sizeof ( uip6_entry - > ip6dst ) ) ;
ip6_fill_mask ( uip6_mask - > ip6dst ) ;
}
if ( spec . match_flags & EFX_FILTER_MATCH_REM_HOST ) {
memcpy ( uip6_entry - > ip6src , spec . rem_host ,
sizeof ( uip6_entry - > ip6src ) ) ;
ip6_fill_mask ( uip6_mask - > ip6src ) ;
}
2012-10-27 00:33:28 +01:00
} else {
/* The above should handle all filters that we insert */
WARN_ON ( 1 ) ;
return - EINVAL ;
2012-01-03 12:05:47 +00:00
}
2012-10-27 00:33:28 +01:00
if ( spec . match_flags & EFX_FILTER_MATCH_OUTER_VID ) {
rule - > flow_type | = FLOW_EXT ;
rule - > h_ext . vlan_tci = spec . outer_vid ;
rule - > m_ext . vlan_tci = htons ( 0xfff ) ;
2012-01-03 12:05:47 +00:00
}
2012-10-27 00:33:28 +01:00
2018-03-08 15:45:17 +00:00
if ( spec . flags & EFX_FILTER_FLAG_RX_RSS ) {
rule - > flow_type | = FLOW_RSS ;
* rss_context = spec . rss_context ;
}
2012-01-03 12:05:47 +00:00
return rc ;
}
2010-06-30 05:06:28 +00:00
static int
efx_ethtool_get_rxnfc ( struct net_device * net_dev ,
2012-01-03 12:05:47 +00:00
struct ethtool_rxnfc * info , u32 * rule_locs )
2010-06-30 05:06:28 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2018-03-08 15:45:17 +00:00
u32 rss_context = 0 ;
2018-03-27 17:44:36 +01:00
s32 rc = 0 ;
2010-06-30 05:06:28 +00:00
switch ( info - > cmd ) {
case ETHTOOL_GRXRINGS :
info - > data = efx - > n_rx_channels ;
return 0 ;
case ETHTOOL_GRXFH : {
2018-03-08 15:45:17 +00:00
struct efx_rss_context * ctx = & efx - > rss_context ;
2020-03-10 19:41:41 -07:00
__u64 data ;
2018-03-08 15:45:17 +00:00
2018-03-27 17:44:36 +01:00
mutex_lock ( & efx - > rss_lock ) ;
2018-03-08 15:45:17 +00:00
if ( info - > flow_type & FLOW_RSS & & info - > rss_context ) {
2018-03-27 17:44:36 +01:00
ctx = efx_find_rss_context_entry ( efx , info - > rss_context ) ;
if ( ! ctx ) {
rc = - ENOENT ;
goto out_unlock ;
}
2018-03-08 15:45:17 +00:00
}
2020-03-10 19:41:41 -07:00
data = 0 ;
2018-03-08 15:45:17 +00:00
if ( ! efx_rss_active ( ctx ) ) /* No RSS */
2020-03-10 19:41:41 -07:00
goto out_setdata_unlock ;
2018-03-08 15:45:17 +00:00
switch ( info - > flow_type & ~ FLOW_RSS ) {
2016-11-03 22:12:58 +00:00
case UDP_V4_FLOW :
case UDP_V6_FLOW :
2018-03-08 15:45:17 +00:00
if ( ctx - > rx_hash_udp_4tuple )
2020-03-10 19:41:41 -07:00
data = ( RXH_L4_B_0_1 | RXH_L4_B_2_3 |
RXH_IP_SRC | RXH_IP_DST ) ;
else
data = RXH_IP_SRC | RXH_IP_DST ;
break ;
case TCP_V4_FLOW :
2010-06-30 05:06:28 +00:00
case TCP_V6_FLOW :
2020-03-10 19:41:41 -07:00
data = ( RXH_L4_B_0_1 | RXH_L4_B_2_3 |
RXH_IP_SRC | RXH_IP_DST ) ;
break ;
case SCTP_V4_FLOW :
2010-06-30 05:06:28 +00:00
case SCTP_V6_FLOW :
2020-03-10 19:41:41 -07:00
case AH_ESP_V4_FLOW :
2010-06-30 05:06:28 +00:00
case AH_ESP_V6_FLOW :
2020-03-10 19:41:41 -07:00
case IPV4_FLOW :
2010-06-30 05:06:28 +00:00
case IPV6_FLOW :
2020-03-10 19:41:41 -07:00
data = RXH_IP_SRC | RXH_IP_DST ;
2010-06-30 05:06:28 +00:00
break ;
default :
break ;
}
2020-03-10 19:41:41 -07:00
out_setdata_unlock :
info - > data = data ;
2018-03-27 17:44:36 +01:00
out_unlock :
mutex_unlock ( & efx - > rss_lock ) ;
return rc ;
2010-06-30 05:06:28 +00:00
}
2012-01-03 12:05:47 +00:00
case ETHTOOL_GRXCLSRLCNT :
info - > data = efx_filter_get_rx_id_limit ( efx ) ;
if ( info - > data = = 0 )
return - EOPNOTSUPP ;
info - > data | = RX_CLS_LOC_SPECIAL ;
info - > rule_cnt =
efx_filter_count_rx_used ( efx , EFX_FILTER_PRI_MANUAL ) ;
return 0 ;
case ETHTOOL_GRXCLSRULE :
if ( efx_filter_get_rx_id_limit ( efx ) = = 0 )
return - EOPNOTSUPP ;
2018-03-08 15:45:17 +00:00
rc = efx_ethtool_get_class_rule ( efx , & info - > fs , & rss_context ) ;
if ( rc < 0 )
return rc ;
if ( info - > fs . flow_type & FLOW_RSS )
info - > rss_context = rss_context ;
return 0 ;
2012-01-03 12:05:47 +00:00
2018-03-08 15:45:17 +00:00
case ETHTOOL_GRXCLSRLALL :
2012-01-03 12:05:47 +00:00
info - > data = efx_filter_get_rx_id_limit ( efx ) ;
if ( info - > data = = 0 )
return - EOPNOTSUPP ;
rc = efx_filter_get_rx_ids ( efx , EFX_FILTER_PRI_MANUAL ,
rule_locs , info - > rule_cnt ) ;
if ( rc < 0 )
return rc ;
info - > rule_cnt = rc ;
return 0 ;
2010-06-30 05:06:28 +00:00
default :
return - EOPNOTSUPP ;
}
}
2016-02-05 11:16:50 +00:00
static inline bool ip6_mask_is_full ( __be32 mask [ 4 ] )
{
return ! ~ ( mask [ 0 ] & mask [ 1 ] & mask [ 2 ] & mask [ 3 ] ) ;
}
static inline bool ip6_mask_is_empty ( __be32 mask [ 4 ] )
{
return ! ( mask [ 0 ] | mask [ 1 ] | mask [ 2 ] | mask [ 3 ] ) ;
}
2012-01-03 12:05:47 +00:00
static int efx_ethtool_set_class_rule ( struct efx_nic * efx ,
2018-03-08 15:45:17 +00:00
struct ethtool_rx_flow_spec * rule ,
u32 rss_context )
2010-09-20 08:43:42 +00:00
{
2012-01-03 12:05:47 +00:00
struct ethtool_tcpip4_spec * ip_entry = & rule - > h_u . tcp_ip4_spec ;
struct ethtool_tcpip4_spec * ip_mask = & rule - > m_u . tcp_ip4_spec ;
2016-02-05 11:16:50 +00:00
struct ethtool_usrip4_spec * uip_entry = & rule - > h_u . usr_ip4_spec ;
struct ethtool_usrip4_spec * uip_mask = & rule - > m_u . usr_ip4_spec ;
struct ethtool_tcpip6_spec * ip6_entry = & rule - > h_u . tcp_ip6_spec ;
struct ethtool_tcpip6_spec * ip6_mask = & rule - > m_u . tcp_ip6_spec ;
struct ethtool_usrip6_spec * uip6_entry = & rule - > h_u . usr_ip6_spec ;
struct ethtool_usrip6_spec * uip6_mask = & rule - > m_u . usr_ip6_spec ;
2018-03-27 17:44:51 +01:00
u32 flow_type = rule - > flow_type & ~ ( FLOW_EXT | FLOW_RSS ) ;
2012-01-03 12:05:47 +00:00
struct ethhdr * mac_entry = & rule - > h_u . ether_spec ;
struct ethhdr * mac_mask = & rule - > m_u . ether_spec ;
2018-03-08 15:45:17 +00:00
enum efx_filter_flags flags = 0 ;
2012-01-03 12:05:47 +00:00
struct efx_filter_spec spec ;
2010-12-07 19:11:26 +00:00
int rc ;
2010-09-20 08:43:42 +00:00
2012-01-03 12:05:47 +00:00
/* Check that user wants us to choose the location */
2012-09-04 18:57:25 +01:00
if ( rule - > location ! = RX_CLS_LOC_ANY )
2010-09-20 08:43:42 +00:00
return - EINVAL ;
2012-01-03 12:05:47 +00:00
/* Range-check ring_cookie */
if ( rule - > ring_cookie > = efx - > n_rx_channels & &
rule - > ring_cookie ! = RX_CLS_FLOW_DISC )
2010-09-20 08:43:42 +00:00
return - EINVAL ;
2012-01-03 12:05:47 +00:00
/* Check for unsupported extensions */
if ( ( rule - > flow_type & FLOW_EXT ) & &
2012-09-06 02:11:04 +01:00
( rule - > m_ext . vlan_etype | | rule - > m_ext . data [ 0 ] | |
2012-01-03 12:05:47 +00:00
rule - > m_ext . data [ 1 ] ) )
return - EINVAL ;
2018-03-08 15:45:17 +00:00
if ( efx - > rx_scatter )
flags | = EFX_FILTER_FLAG_RX_SCATTER ;
if ( rule - > flow_type & FLOW_RSS )
flags | = EFX_FILTER_FLAG_RX_RSS ;
efx_filter_init_rx ( & spec , EFX_FILTER_PRI_MANUAL , flags ,
2012-01-03 12:05:47 +00:00
( rule - > ring_cookie = = RX_CLS_FLOW_DISC ) ?
2012-10-30 01:01:52 +00:00
EFX_FILTER_RX_DMAQ_ID_DROP : rule - > ring_cookie ) ;
2010-12-07 19:11:26 +00:00
2018-03-08 15:45:17 +00:00
if ( rule - > flow_type & FLOW_RSS )
spec . rss_context = rss_context ;
2018-03-27 17:44:51 +01:00
switch ( flow_type ) {
2010-09-20 08:43:42 +00:00
case TCP_V4_FLOW :
2012-10-27 00:33:28 +01:00
case UDP_V4_FLOW :
spec . match_flags = ( EFX_FILTER_MATCH_ETHER_TYPE |
EFX_FILTER_MATCH_IP_PROTO ) ;
spec . ether_type = htons ( ETH_P_IP ) ;
2018-03-27 17:44:51 +01:00
spec . ip_proto = flow_type = = TCP_V4_FLOW ? IPPROTO_TCP
: IPPROTO_UDP ;
2012-10-27 00:33:28 +01:00
if ( ip_mask - > ip4dst ) {
if ( ip_mask - > ip4dst ! = IP4_ADDR_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_LOC_HOST ;
spec . loc_host [ 0 ] = ip_entry - > ip4dst ;
}
if ( ip_mask - > ip4src ) {
if ( ip_mask - > ip4src ! = IP4_ADDR_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_REM_HOST ;
spec . rem_host [ 0 ] = ip_entry - > ip4src ;
}
if ( ip_mask - > pdst ) {
if ( ip_mask - > pdst ! = PORT_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_LOC_PORT ;
spec . loc_port = ip_entry - > pdst ;
}
if ( ip_mask - > psrc ) {
if ( ip_mask - > psrc ! = PORT_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_REM_PORT ;
spec . rem_port = ip_entry - > psrc ;
}
if ( ip_mask - > tos )
2010-09-20 08:43:42 +00:00
return - EINVAL ;
break ;
2012-02-02 22:41:49 +00:00
2016-02-05 11:16:50 +00:00
case TCP_V6_FLOW :
case UDP_V6_FLOW :
spec . match_flags = ( EFX_FILTER_MATCH_ETHER_TYPE |
EFX_FILTER_MATCH_IP_PROTO ) ;
spec . ether_type = htons ( ETH_P_IPV6 ) ;
2018-03-27 17:44:51 +01:00
spec . ip_proto = flow_type = = TCP_V6_FLOW ? IPPROTO_TCP
: IPPROTO_UDP ;
2016-02-05 11:16:50 +00:00
if ( ! ip6_mask_is_empty ( ip6_mask - > ip6dst ) ) {
if ( ! ip6_mask_is_full ( ip6_mask - > ip6dst ) )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_LOC_HOST ;
memcpy ( spec . loc_host , ip6_entry - > ip6dst , sizeof ( spec . loc_host ) ) ;
}
if ( ! ip6_mask_is_empty ( ip6_mask - > ip6src ) ) {
if ( ! ip6_mask_is_full ( ip6_mask - > ip6src ) )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_REM_HOST ;
memcpy ( spec . rem_host , ip6_entry - > ip6src , sizeof ( spec . rem_host ) ) ;
}
if ( ip6_mask - > pdst ) {
if ( ip6_mask - > pdst ! = PORT_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_LOC_PORT ;
spec . loc_port = ip6_entry - > pdst ;
}
if ( ip6_mask - > psrc ) {
if ( ip6_mask - > psrc ! = PORT_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_REM_PORT ;
spec . rem_port = ip6_entry - > psrc ;
}
if ( ip6_mask - > tclass )
return - EINVAL ;
break ;
case IPV4_USER_FLOW :
if ( uip_mask - > l4_4_bytes | | uip_mask - > tos | | uip_mask - > ip_ver | |
uip_entry - > ip_ver ! = ETH_RX_NFC_IP4 )
return - EINVAL ;
spec . match_flags = EFX_FILTER_MATCH_ETHER_TYPE ;
spec . ether_type = htons ( ETH_P_IP ) ;
if ( uip_mask - > ip4dst ) {
if ( uip_mask - > ip4dst ! = IP4_ADDR_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_LOC_HOST ;
spec . loc_host [ 0 ] = uip_entry - > ip4dst ;
}
if ( uip_mask - > ip4src ) {
if ( uip_mask - > ip4src ! = IP4_ADDR_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_REM_HOST ;
spec . rem_host [ 0 ] = uip_entry - > ip4src ;
}
if ( uip_mask - > proto ) {
if ( uip_mask - > proto ! = IP_PROTO_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_IP_PROTO ;
spec . ip_proto = uip_entry - > proto ;
}
break ;
case IPV6_USER_FLOW :
if ( uip6_mask - > l4_4_bytes | | uip6_mask - > tclass )
return - EINVAL ;
spec . match_flags = EFX_FILTER_MATCH_ETHER_TYPE ;
spec . ether_type = htons ( ETH_P_IPV6 ) ;
if ( ! ip6_mask_is_empty ( uip6_mask - > ip6dst ) ) {
if ( ! ip6_mask_is_full ( uip6_mask - > ip6dst ) )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_LOC_HOST ;
memcpy ( spec . loc_host , uip6_entry - > ip6dst , sizeof ( spec . loc_host ) ) ;
}
if ( ! ip6_mask_is_empty ( uip6_mask - > ip6src ) ) {
if ( ! ip6_mask_is_full ( uip6_mask - > ip6src ) )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_REM_HOST ;
memcpy ( spec . rem_host , uip6_entry - > ip6src , sizeof ( spec . rem_host ) ) ;
}
if ( uip6_mask - > l4_proto ) {
if ( uip6_mask - > l4_proto ! = IP_PROTO_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_IP_PROTO ;
spec . ip_proto = uip6_entry - > l4_proto ;
}
break ;
2012-10-27 00:33:28 +01:00
case ETHER_FLOW :
if ( ! is_zero_ether_addr ( mac_mask - > h_dest ) ) {
if ( ether_addr_equal ( mac_mask - > h_dest ,
mac_addr_ig_mask ) )
spec . match_flags | = EFX_FILTER_MATCH_LOC_MAC_IG ;
else if ( is_broadcast_ether_addr ( mac_mask - > h_dest ) )
spec . match_flags | = EFX_FILTER_MATCH_LOC_MAC ;
2012-02-02 22:41:49 +00:00
else
2012-10-27 00:33:28 +01:00
return - EINVAL ;
2014-03-07 18:27:41 +00:00
ether_addr_copy ( spec . loc_mac , mac_entry - > h_dest ) ;
2012-02-02 22:41:49 +00:00
}
2012-10-27 00:33:28 +01:00
if ( ! is_zero_ether_addr ( mac_mask - > h_source ) ) {
if ( ! is_broadcast_ether_addr ( mac_mask - > h_source ) )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_REM_MAC ;
2014-03-07 18:27:41 +00:00
ether_addr_copy ( spec . rem_mac , mac_entry - > h_source ) ;
2012-10-27 00:33:28 +01:00
}
if ( mac_mask - > h_proto ) {
if ( mac_mask - > h_proto ! = ETHER_TYPE_FULL_MASK )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_ETHER_TYPE ;
spec . ether_type = mac_entry - > h_proto ;
2012-02-02 22:41:49 +00:00
}
2010-09-20 08:43:42 +00:00
break ;
2010-12-07 19:11:26 +00:00
2010-09-20 08:43:42 +00:00
default :
return - EINVAL ;
}
2012-10-27 00:33:28 +01:00
if ( ( rule - > flow_type & FLOW_EXT ) & & rule - > m_ext . vlan_tci ) {
if ( rule - > m_ext . vlan_tci ! = htons ( 0xfff ) )
return - EINVAL ;
spec . match_flags | = EFX_FILTER_MATCH_OUTER_VID ;
spec . outer_vid = rule - > h_ext . vlan_tci ;
}
2012-01-03 12:05:47 +00:00
rc = efx_filter_insert_filter ( efx , & spec , true ) ;
if ( rc < 0 )
return rc ;
2011-05-14 02:35:25 +01:00
2012-01-03 12:05:47 +00:00
rule - > location = rc ;
return 0 ;
}
static int efx_ethtool_set_rxnfc ( struct net_device * net_dev ,
struct ethtool_rxnfc * info )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
if ( efx_filter_get_rx_id_limit ( efx ) = = 0 )
return - EOPNOTSUPP ;
switch ( info - > cmd ) {
case ETHTOOL_SRXCLSRLINS :
2018-03-08 15:45:17 +00:00
return efx_ethtool_set_class_rule ( efx , & info - > fs ,
info - > rss_context ) ;
2012-01-03 12:05:47 +00:00
case ETHTOOL_SRXCLSRLDEL :
return efx_filter_remove_id_safe ( efx , EFX_FILTER_PRI_MANUAL ,
info - > fs . location ) ;
default :
return - EOPNOTSUPP ;
}
2010-09-20 08:43:42 +00:00
}
2011-12-15 13:55:01 +00:00
static u32 efx_ethtool_get_rxfh_indir_size ( struct net_device * net_dev )
2010-06-30 05:06:28 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2018-03-08 15:45:17 +00:00
if ( efx - > n_rx_channels = = 1 )
return 0 ;
return ARRAY_SIZE ( efx - > rss_context . rx_indir_table ) ;
2011-12-15 13:55:01 +00:00
}
2017-01-17 12:01:53 +00:00
static u32 efx_ethtool_get_rxfh_key_size ( struct net_device * net_dev )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
return efx - > type - > rx_hash_key_size ;
}
2014-12-02 18:12:10 +02:00
static int efx_ethtool_get_rxfh ( struct net_device * net_dev , u32 * indir , u8 * key ,
u8 * hfunc )
2011-12-15 13:55:01 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2017-01-17 12:02:12 +00:00
int rc ;
rc = efx - > type - > rx_pull_rss_config ( efx ) ;
if ( rc )
return rc ;
2010-06-30 05:06:28 +00:00
2014-12-02 18:12:10 +02:00
if ( hfunc )
* hfunc = ETH_RSS_HASH_TOP ;
if ( indir )
2018-03-08 15:45:17 +00:00
memcpy ( indir , efx - > rss_context . rx_indir_table ,
sizeof ( efx - > rss_context . rx_indir_table ) ) ;
2017-01-17 12:01:53 +00:00
if ( key )
2018-03-08 15:45:17 +00:00
memcpy ( key , efx - > rss_context . rx_hash_key ,
efx - > type - > rx_hash_key_size ) ;
2010-06-30 05:06:28 +00:00
return 0 ;
}
2014-12-02 18:12:10 +02:00
static int efx_ethtool_set_rxfh ( struct net_device * net_dev , const u32 * indir ,
const u8 * key , const u8 hfunc )
2010-06-30 05:06:28 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2017-01-17 12:01:53 +00:00
/* Hash function is Toeplitz, cannot be changed */
if ( hfunc ! = ETH_RSS_HASH_NO_CHANGE & & hfunc ! = ETH_RSS_HASH_TOP )
2014-12-02 18:12:10 +02:00
return - EOPNOTSUPP ;
2017-01-17 12:01:53 +00:00
if ( ! indir & & ! key )
2014-12-02 18:12:10 +02:00
return 0 ;
2015-05-06 00:59:38 +01:00
2017-01-17 12:01:53 +00:00
if ( ! key )
2018-03-08 15:45:17 +00:00
key = efx - > rss_context . rx_hash_key ;
2017-01-17 12:01:53 +00:00
if ( ! indir )
2018-03-08 15:45:17 +00:00
indir = efx - > rss_context . rx_indir_table ;
2017-01-17 12:01:53 +00:00
return efx - > type - > rx_push_rss_config ( efx , true , indir , key ) ;
2010-06-30 05:06:28 +00:00
}
2018-03-08 15:45:17 +00:00
static int efx_ethtool_get_rxfh_context ( struct net_device * net_dev , u32 * indir ,
u8 * key , u8 * hfunc , u32 rss_context )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
struct efx_rss_context * ctx ;
2018-03-27 17:44:36 +01:00
int rc = 0 ;
2018-03-08 15:45:17 +00:00
if ( ! efx - > type - > rx_pull_rss_context_config )
return - EOPNOTSUPP ;
2018-03-27 17:44:36 +01:00
mutex_lock ( & efx - > rss_lock ) ;
ctx = efx_find_rss_context_entry ( efx , rss_context ) ;
if ( ! ctx ) {
rc = - ENOENT ;
goto out_unlock ;
}
2018-03-08 15:45:17 +00:00
rc = efx - > type - > rx_pull_rss_context_config ( efx , ctx ) ;
if ( rc )
2018-03-27 17:44:36 +01:00
goto out_unlock ;
2018-03-08 15:45:17 +00:00
if ( hfunc )
* hfunc = ETH_RSS_HASH_TOP ;
if ( indir )
memcpy ( indir , ctx - > rx_indir_table , sizeof ( ctx - > rx_indir_table ) ) ;
if ( key )
memcpy ( key , ctx - > rx_hash_key , efx - > type - > rx_hash_key_size ) ;
2018-03-27 17:44:36 +01:00
out_unlock :
mutex_unlock ( & efx - > rss_lock ) ;
return rc ;
2018-03-08 15:45:17 +00:00
}
static int efx_ethtool_set_rxfh_context ( struct net_device * net_dev ,
const u32 * indir , const u8 * key ,
const u8 hfunc , u32 * rss_context ,
bool delete )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
struct efx_rss_context * ctx ;
bool allocated = false ;
int rc ;
if ( ! efx - > type - > rx_push_rss_context_config )
return - EOPNOTSUPP ;
/* Hash function is Toeplitz, cannot be changed */
if ( hfunc ! = ETH_RSS_HASH_NO_CHANGE & & hfunc ! = ETH_RSS_HASH_TOP )
return - EOPNOTSUPP ;
2018-03-27 17:44:36 +01:00
mutex_lock ( & efx - > rss_lock ) ;
2018-03-08 15:45:17 +00:00
if ( * rss_context = = ETH_RXFH_CONTEXT_ALLOC ) {
2018-03-27 17:44:36 +01:00
if ( delete ) {
2018-03-08 15:45:17 +00:00
/* alloc + delete == Nothing to do */
2018-03-27 17:44:36 +01:00
rc = - EINVAL ;
goto out_unlock ;
}
ctx = efx_alloc_rss_context_entry ( efx ) ;
if ( ! ctx ) {
rc = - ENOMEM ;
goto out_unlock ;
}
2020-01-10 13:28:45 +00:00
ctx - > context_id = EFX_MCDI_RSS_CONTEXT_INVALID ;
2018-03-08 15:45:17 +00:00
/* Initialise indir table and key to defaults */
efx_set_default_rx_indir_table ( efx , ctx ) ;
netdev_rss_key_fill ( ctx - > rx_hash_key , sizeof ( ctx - > rx_hash_key ) ) ;
allocated = true ;
} else {
2018-03-27 17:44:36 +01:00
ctx = efx_find_rss_context_entry ( efx , * rss_context ) ;
if ( ! ctx ) {
rc = - ENOENT ;
goto out_unlock ;
}
2018-03-08 15:45:17 +00:00
}
if ( delete ) {
/* delete this context */
rc = efx - > type - > rx_push_rss_context_config ( efx , ctx , NULL , NULL ) ;
if ( ! rc )
efx_free_rss_context_entry ( ctx ) ;
2018-03-27 17:44:36 +01:00
goto out_unlock ;
2018-03-08 15:45:17 +00:00
}
if ( ! key )
key = ctx - > rx_hash_key ;
if ( ! indir )
indir = ctx - > rx_indir_table ;
rc = efx - > type - > rx_push_rss_context_config ( efx , ctx , indir , key ) ;
if ( rc & & allocated )
efx_free_rss_context_entry ( ctx ) ;
else
* rss_context = ctx - > user_id ;
2018-03-27 17:44:36 +01:00
out_unlock :
mutex_unlock ( & efx - > rss_lock ) ;
2018-03-08 15:45:17 +00:00
return rc ;
}
2013-07-03 14:07:40 +08:00
static int efx_ethtool_get_ts_info ( struct net_device * net_dev ,
struct ethtool_ts_info * ts_info )
2013-04-08 17:34:58 +01:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
/* Software capabilities */
ts_info - > so_timestamping = ( SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE ) ;
ts_info - > phc_index = - 1 ;
efx_ptp_get_ts_info ( efx , ts_info ) ;
return 0 ;
}
2012-05-01 18:50:43 +01:00
static int efx_ethtool_get_module_eeprom ( struct net_device * net_dev ,
struct ethtool_eeprom * ee ,
u8 * data )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
int ret ;
if ( ! efx - > phy_op | | ! efx - > phy_op - > get_module_eeprom )
return - EOPNOTSUPP ;
mutex_lock ( & efx - > mac_lock ) ;
ret = efx - > phy_op - > get_module_eeprom ( efx , ee , data ) ;
mutex_unlock ( & efx - > mac_lock ) ;
return ret ;
}
static int efx_ethtool_get_module_info ( struct net_device * net_dev ,
struct ethtool_modinfo * modinfo )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
int ret ;
if ( ! efx - > phy_op | | ! efx - > phy_op - > get_module_info )
return - EOPNOTSUPP ;
mutex_lock ( & efx - > mac_lock ) ;
ret = efx - > phy_op - > get_module_info ( efx , modinfo ) ;
mutex_unlock ( & efx - > mac_lock ) ;
return ret ;
}
2018-03-14 14:21:26 +00:00
static int efx_ethtool_get_fecparam ( struct net_device * net_dev ,
struct ethtool_fecparam * fecparam )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
int rc ;
if ( ! efx - > phy_op | | ! efx - > phy_op - > get_fecparam )
return - EOPNOTSUPP ;
mutex_lock ( & efx - > mac_lock ) ;
rc = efx - > phy_op - > get_fecparam ( efx , fecparam ) ;
mutex_unlock ( & efx - > mac_lock ) ;
return rc ;
}
static int efx_ethtool_set_fecparam ( struct net_device * net_dev ,
struct ethtool_fecparam * fecparam )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
int rc ;
if ( ! efx - > phy_op | | ! efx - > phy_op - > get_fecparam )
return - EOPNOTSUPP ;
mutex_lock ( & efx - > mac_lock ) ;
rc = efx - > phy_op - > set_fecparam ( efx , fecparam ) ;
mutex_unlock ( & efx - > mac_lock ) ;
return rc ;
}
2009-09-02 01:03:33 -07:00
const struct ethtool_ops efx_ethtool_ops = {
2008-04-27 12:55:59 +01:00
. get_drvinfo = efx_ethtool_get_drvinfo ,
2010-06-21 03:06:53 +00:00
. get_regs_len = efx_ethtool_get_regs_len ,
. get_regs = efx_ethtool_get_regs ,
2010-06-23 11:30:07 +00:00
. get_msglevel = efx_ethtool_get_msglevel ,
. set_msglevel = efx_ethtool_set_msglevel ,
2008-04-27 12:55:59 +01:00
. nway_reset = efx_ethtool_nway_reset ,
2010-12-09 12:10:25 +00:00
. get_link = ethtool_op_get_link ,
2008-04-27 12:55:59 +01:00
. get_coalesce = efx_ethtool_get_coalesce ,
. set_coalesce = efx_ethtool_set_coalesce ,
2010-09-10 06:42:33 +00:00
. get_ringparam = efx_ethtool_get_ringparam ,
. set_ringparam = efx_ethtool_set_ringparam ,
2008-04-27 12:55:59 +01:00
. get_pauseparam = efx_ethtool_get_pauseparam ,
. set_pauseparam = efx_ethtool_set_pauseparam ,
2008-09-01 12:48:12 +01:00
. get_sset_count = efx_ethtool_get_sset_count ,
2008-05-07 13:36:19 +01:00
. self_test = efx_ethtool_self_test ,
2008-04-27 12:55:59 +01:00
. get_strings = efx_ethtool_get_strings ,
2011-04-02 00:43:46 +01:00
. set_phys_id = efx_ethtool_phys_id ,
2008-04-27 12:55:59 +01:00
. get_ethtool_stats = efx_ethtool_get_stats ,
2009-11-29 03:43:07 +00:00
. get_wol = efx_ethtool_get_wol ,
. set_wol = efx_ethtool_set_wol ,
2009-11-29 03:43:15 +00:00
. reset = efx_ethtool_reset ,
2010-06-30 05:06:28 +00:00
. get_rxnfc = efx_ethtool_get_rxnfc ,
2012-01-03 12:05:47 +00:00
. set_rxnfc = efx_ethtool_set_rxnfc ,
2011-12-15 13:55:01 +00:00
. get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size ,
2017-01-17 12:01:53 +00:00
. get_rxfh_key_size = efx_ethtool_get_rxfh_key_size ,
2014-05-15 01:25:27 +01:00
. get_rxfh = efx_ethtool_get_rxfh ,
. set_rxfh = efx_ethtool_set_rxfh ,
2018-03-08 15:45:17 +00:00
. get_rxfh_context = efx_ethtool_get_rxfh_context ,
. set_rxfh_context = efx_ethtool_set_rxfh_context ,
2013-04-08 17:34:58 +01:00
. get_ts_info = efx_ethtool_get_ts_info ,
2012-05-01 18:50:43 +01:00
. get_module_info = efx_ethtool_get_module_info ,
. get_module_eeprom = efx_ethtool_get_module_eeprom ,
2016-12-15 00:12:53 +01:00
. get_link_ksettings = efx_ethtool_get_link_ksettings ,
. set_link_ksettings = efx_ethtool_set_link_ksettings ,
2018-03-14 14:21:26 +00:00
. get_fecparam = efx_ethtool_get_fecparam ,
. set_fecparam = efx_ethtool_set_fecparam ,
2008-04-27 12:55:59 +01:00
} ;