@ -36,236 +36,10 @@ u32 ethtool_op_get_link(struct net_device *dev)
}
EXPORT_SYMBOL ( ethtool_op_get_link ) ;
u32 ethtool_op_get_tx_csum ( struct net_device * dev )
{
return ( dev - > features & NETIF_F_ALL_CSUM ) ! = 0 ;
}
EXPORT_SYMBOL ( ethtool_op_get_tx_csum ) ;
int ethtool_op_set_tx_csum ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_IP_CSUM ;
else
dev - > features & = ~ NETIF_F_IP_CSUM ;
return 0 ;
}
EXPORT_SYMBOL ( ethtool_op_set_tx_csum ) ;
int ethtool_op_set_tx_hw_csum ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_HW_CSUM ;
else
dev - > features & = ~ NETIF_F_HW_CSUM ;
return 0 ;
}
EXPORT_SYMBOL ( ethtool_op_set_tx_hw_csum ) ;
int ethtool_op_set_tx_ipv6_csum ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ;
else
dev - > features & = ~ ( NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ) ;
return 0 ;
}
EXPORT_SYMBOL ( ethtool_op_set_tx_ipv6_csum ) ;
u32 ethtool_op_get_sg ( struct net_device * dev )
{
return ( dev - > features & NETIF_F_SG ) ! = 0 ;
}
EXPORT_SYMBOL ( ethtool_op_get_sg ) ;
int ethtool_op_set_sg ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_SG ;
else
dev - > features & = ~ NETIF_F_SG ;
return 0 ;
}
EXPORT_SYMBOL ( ethtool_op_set_sg ) ;
u32 ethtool_op_get_tso ( struct net_device * dev )
{
return ( dev - > features & NETIF_F_TSO ) ! = 0 ;
}
EXPORT_SYMBOL ( ethtool_op_get_tso ) ;
int ethtool_op_set_tso ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_TSO ;
else
dev - > features & = ~ NETIF_F_TSO ;
return 0 ;
}
EXPORT_SYMBOL ( ethtool_op_set_tso ) ;
u32 ethtool_op_get_ufo ( struct net_device * dev )
{
return ( dev - > features & NETIF_F_UFO ) ! = 0 ;
}
EXPORT_SYMBOL ( ethtool_op_get_ufo ) ;
int ethtool_op_set_ufo ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_UFO ;
else
dev - > features & = ~ NETIF_F_UFO ;
return 0 ;
}
EXPORT_SYMBOL ( ethtool_op_set_ufo ) ;
/* the following list of flags are the same as their associated
* NETIF_F_xxx values in include / linux / netdevice . h
*/
static const u32 flags_dup_features =
( ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE |
ETH_FLAG_RXHASH ) ;
u32 ethtool_op_get_flags ( struct net_device * dev )
{
/* in the future, this function will probably contain additional
* handling for flags which are not so easily handled
* by a simple masking operation
*/
return dev - > features & flags_dup_features ;
}
EXPORT_SYMBOL ( ethtool_op_get_flags ) ;
/* Check if device can enable (or disable) particular feature coded in "data"
* argument . Flags " supported " describe features that can be toggled by device .
* If feature can not be toggled , it state ( enabled or disabled ) must match
* hardcoded device features state , otherwise flags are marked as invalid .
*/
bool ethtool_invalid_flags ( struct net_device * dev , u32 data , u32 supported )
{
u32 features = dev - > features & flags_dup_features ;
/* "data" can contain only flags_dup_features bits,
* see __ethtool_set_flags */
return ( features & ~ supported ) ! = ( data & ~ supported ) ;
}
EXPORT_SYMBOL ( ethtool_invalid_flags ) ;
int ethtool_op_set_flags ( struct net_device * dev , u32 data , u32 supported )
{
if ( ethtool_invalid_flags ( dev , data , supported ) )
return - EINVAL ;
dev - > features = ( ( dev - > features & ~ flags_dup_features ) |
( data & flags_dup_features ) ) ;
return 0 ;
}
EXPORT_SYMBOL ( ethtool_op_set_flags ) ;
/* Handlers for each ethtool command */
# define ETHTOOL_DEV_FEATURE_WORDS 1
static void ethtool_get_features_compat ( struct net_device * dev ,
struct ethtool_get_features_block * features )
{
if ( ! dev - > ethtool_ops )
return ;
/* getting RX checksum */
if ( dev - > ethtool_ops - > get_rx_csum )
if ( dev - > ethtool_ops - > get_rx_csum ( dev ) )
features [ 0 ] . active | = NETIF_F_RXCSUM ;
/* mark legacy-changeable features */
if ( dev - > ethtool_ops - > set_sg )
features [ 0 ] . available | = NETIF_F_SG ;
if ( dev - > ethtool_ops - > set_tx_csum )
features [ 0 ] . available | = NETIF_F_ALL_CSUM ;
if ( dev - > ethtool_ops - > set_tso )
features [ 0 ] . available | = NETIF_F_ALL_TSO ;
if ( dev - > ethtool_ops - > set_rx_csum )
features [ 0 ] . available | = NETIF_F_RXCSUM ;
if ( dev - > ethtool_ops - > set_flags )
features [ 0 ] . available | = flags_dup_features ;
}
static int ethtool_set_feature_compat ( struct net_device * dev ,
int ( * legacy_set ) ( struct net_device * , u32 ) ,
struct ethtool_set_features_block * features , u32 mask )
{
u32 do_set ;
if ( ! legacy_set )
return 0 ;
if ( ! ( features [ 0 ] . valid & mask ) )
return 0 ;
features [ 0 ] . valid & = ~ mask ;
do_set = ! ! ( features [ 0 ] . requested & mask ) ;
if ( legacy_set ( dev , do_set ) < 0 )
netdev_info ( dev ,
" Legacy feature change (%s) failed for 0x%08x \n " ,
do_set ? " set " : " clear " , mask ) ;
return 1 ;
}
static int ethtool_set_flags_compat ( struct net_device * dev ,
int ( * legacy_set ) ( struct net_device * , u32 ) ,
struct ethtool_set_features_block * features , u32 mask )
{
u32 value ;
if ( ! legacy_set )
return 0 ;
if ( ! ( features [ 0 ] . valid & mask ) )
return 0 ;
value = dev - > features & ~ features [ 0 ] . valid ;
value | = features [ 0 ] . requested ;
features [ 0 ] . valid & = ~ mask ;
if ( legacy_set ( dev , value & mask ) < 0 )
netdev_info ( dev , " Legacy flags change failed \n " ) ;
return 1 ;
}
static int ethtool_set_features_compat ( struct net_device * dev ,
struct ethtool_set_features_block * features )
{
int compat ;
if ( ! dev - > ethtool_ops )
return 0 ;
compat = ethtool_set_feature_compat ( dev , dev - > ethtool_ops - > set_sg ,
features , NETIF_F_SG ) ;
compat | = ethtool_set_feature_compat ( dev , dev - > ethtool_ops - > set_tx_csum ,
features , NETIF_F_ALL_CSUM ) ;
compat | = ethtool_set_feature_compat ( dev , dev - > ethtool_ops - > set_tso ,
features , NETIF_F_ALL_TSO ) ;
compat | = ethtool_set_feature_compat ( dev , dev - > ethtool_ops - > set_rx_csum ,
features , NETIF_F_RXCSUM ) ;
compat | = ethtool_set_flags_compat ( dev , dev - > ethtool_ops - > set_flags ,
features , flags_dup_features ) ;
return compat ;
}
static int ethtool_get_features ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_gfeatures cmd = {
@ -283,8 +57,6 @@ static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
u32 __user * sizeaddr ;
u32 copy_size ;
ethtool_get_features_compat ( dev , features ) ;
sizeaddr = useraddr + offsetof ( struct ethtool_gfeatures , size ) ;
if ( get_user ( copy_size , sizeaddr ) )
return - EFAULT ;
@ -320,9 +92,6 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
if ( features [ 0 ] . valid & ~ NETIF_F_ETHTOOL_BITS )
return - EINVAL ;
if ( ethtool_set_features_compat ( dev , features ) )
ret | = ETHTOOL_F_COMPAT ;
if ( features [ 0 ] . valid & ~ dev - > hw_features ) {
features [ 0 ] . valid & = dev - > hw_features ;
ret | = ETHTOOL_F_UNSUPPORTED ;
@ -433,34 +202,6 @@ static u32 ethtool_get_feature_mask(u32 eth_cmd)
}
}
static void * __ethtool_get_one_feature_actor ( struct net_device * dev , u32 ethcmd )
{
const struct ethtool_ops * ops = dev - > ethtool_ops ;
if ( ! ops )
return NULL ;
switch ( ethcmd ) {
case ETHTOOL_GTXCSUM :
return ops - > get_tx_csum ;
case ETHTOOL_GRXCSUM :
return ops - > get_rx_csum ;
case ETHTOOL_SSG :
return ops - > get_sg ;
case ETHTOOL_STSO :
return ops - > get_tso ;
case ETHTOOL_SUFO :
return ops - > get_ufo ;
default :
return NULL ;
}
}
static u32 __ethtool_get_rx_csum_oldbug ( struct net_device * dev )
{
return ! ! ( dev - > features & NETIF_F_ALL_CSUM ) ;
}
static int ethtool_get_one_feature ( struct net_device * dev ,
char __user * useraddr , u32 ethcmd )
{
@ -470,31 +211,11 @@ static int ethtool_get_one_feature(struct net_device *dev,
. data = ! ! ( dev - > features & mask ) ,
} ;
/* compatibility with discrete get_ ops */
if ( ! ( dev - > hw_features & mask ) ) {
u32 ( * actor ) ( struct net_device * ) ;
actor = __ethtool_get_one_feature_actor ( dev , ethcmd ) ;
/* bug compatibility with old get_rx_csum */
if ( ethcmd = = ETHTOOL_GRXCSUM & & ! actor )
actor = __ethtool_get_rx_csum_oldbug ;
if ( actor )
edata . data = actor ( dev ) ;
}
if ( copy_to_user ( useraddr , & edata , sizeof ( edata ) ) )
return - EFAULT ;
return 0 ;
}
static int __ethtool_set_tx_csum ( struct net_device * dev , u32 data ) ;
static int __ethtool_set_rx_csum ( struct net_device * dev , u32 data ) ;
static int __ethtool_set_sg ( struct net_device * dev , u32 data ) ;
static int __ethtool_set_tso ( struct net_device * dev , u32 data ) ;
static int __ethtool_set_ufo ( struct net_device * dev , u32 data ) ;
static int ethtool_set_one_feature ( struct net_device * dev ,
void __user * useraddr , u32 ethcmd )
{
@ -506,56 +227,38 @@ static int ethtool_set_one_feature(struct net_device *dev,
mask = ethtool_get_feature_mask ( ethcmd ) ;
mask & = dev - > hw_features ;
if ( mask ) {
if ( edata . data )
dev - > wanted_features | = mask ;
else
dev - > wanted_features & = ~ mask ;
if ( ! mask )
return - EOPNOTSUPP ;
__netdev_update_features ( dev ) ;
return 0 ;
}
if ( edata . data )
dev - > wanted_features | = mask ;
else
dev - > wanted_features & = ~ mask ;
/* Driver is not converted to ndo_fix_features or does not
* support changing this offload . In the latter case it won ' t
* have corresponding ethtool_ops field set .
*
* Following part is to be removed after all drivers advertise
* their changeable features in netdev - > hw_features and stop
* using discrete offload setting ops .
*/
__netdev_update_features ( dev ) ;
switch ( ethcmd ) {
case ETHTOOL_STXCSUM :
return __ethtool_set_tx_csum ( dev , edata . data ) ;
case ETHTOOL_SRXCSUM :
return __ethtool_set_rx_csum ( dev , edata . data ) ;
case ETHTOOL_SSG :
return __ethtool_set_sg ( dev , edata . data ) ;
case ETHTOOL_STSO :
return __ethtool_set_tso ( dev , edata . data ) ;
case ETHTOOL_SUFO :
return __ethtool_set_ufo ( dev , edata . data ) ;
default :
return - EOPNOTSUPP ;
}
return 0 ;
}
/* the following list of flags are the same as their associated
* NETIF_F_xxx values in include / linux / netdevice . h
*/
static const u32 flags_dup_features =
( ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE |
ETH_FLAG_RXHASH ) ;
static u32 __ethtool_get_flags ( struct net_device * dev )
{
return dev - > features & flags_dup_features ;
}
int __ethtool_set_flags ( struct net_device * dev , u32 data )
static int __ethtool_set_flags ( struct net_device * dev , u32 data )
{
u32 changed ;
if ( data & ~ flags_dup_features )
return - EINVAL ;
/* legacy set_flags() op */
if ( dev - > ethtool_ops - > set_flags ) {
if ( unlikely ( dev - > hw_features & flags_dup_features ) )
netdev_warn ( dev ,
" driver BUG: mixed hw_features and set_flags() \n " ) ;
return dev - > ethtool_ops - > set_flags ( dev , data ) ;
}
/* allow changing only bits set in hw_features */
changed = ( data ^ dev - > features ) & flags_dup_features ;
if ( changed & ~ dev - > hw_features )
@ -1231,81 +934,6 @@ static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
return dev - > ethtool_ops - > set_pauseparam ( dev , & pauseparam ) ;
}
static int __ethtool_set_sg ( struct net_device * dev , u32 data )
{
int err ;
if ( ! dev - > ethtool_ops - > set_sg )
return - EOPNOTSUPP ;
if ( data & & ! ( dev - > features & NETIF_F_ALL_CSUM ) )
return - EINVAL ;
if ( ! data & & dev - > ethtool_ops - > set_tso ) {
err = dev - > ethtool_ops - > set_tso ( dev , 0 ) ;
if ( err )
return err ;
}
if ( ! data & & dev - > ethtool_ops - > set_ufo ) {
err = dev - > ethtool_ops - > set_ufo ( dev , 0 ) ;
if ( err )
return err ;
}
return dev - > ethtool_ops - > set_sg ( dev , data ) ;
}
static int __ethtool_set_tx_csum ( struct net_device * dev , u32 data )
{
int err ;
if ( ! dev - > ethtool_ops - > set_tx_csum )
return - EOPNOTSUPP ;
if ( ! data & & dev - > ethtool_ops - > set_sg ) {
err = __ethtool_set_sg ( dev , 0 ) ;
if ( err )
return err ;
}
return dev - > ethtool_ops - > set_tx_csum ( dev , data ) ;
}
static int __ethtool_set_rx_csum ( struct net_device * dev , u32 data )
{
if ( ! dev - > ethtool_ops - > set_rx_csum )
return - EOPNOTSUPP ;
if ( ! data )
dev - > features & = ~ NETIF_F_GRO ;
return dev - > ethtool_ops - > set_rx_csum ( dev , data ) ;
}
static int __ethtool_set_tso ( struct net_device * dev , u32 data )
{
if ( ! dev - > ethtool_ops - > set_tso )
return - EOPNOTSUPP ;
if ( data & & ! ( dev - > features & NETIF_F_SG ) )
return - EINVAL ;
return dev - > ethtool_ops - > set_tso ( dev , data ) ;
}
static int __ethtool_set_ufo ( struct net_device * dev , u32 data )
{
if ( ! dev - > ethtool_ops - > set_ufo )
return - EOPNOTSUPP ;
if ( data & & ! ( dev - > features & NETIF_F_SG ) )
return - EINVAL ;
if ( data & & ! ( ( dev - > features & NETIF_F_GEN_CSUM ) | |
( dev - > features & ( NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ) )
= = ( NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ) ) )
return - EINVAL ;
return dev - > ethtool_ops - > set_ufo ( dev , data ) ;
}
static int ethtool_self_test ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_test test ;
@ -1771,9 +1399,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
break ;
case ETHTOOL_GFLAGS :
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
( dev - > ethtool_ops - > get_flags ?
dev - > ethtool_ops - > get_flags :
ethtool_op_get_flags ) ) ;
__ethtool_get_flags ) ;
break ;
case ETHTOOL_SFLAGS :
rc = ethtool_set_value ( dev , useraddr , __ethtool_set_flags ) ;