2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* net/core/ethtool.c - Ethtool ioctl handler
|
|
|
|
* Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
|
|
|
|
*
|
|
|
|
* This file is where we call all the ethtool_ops commands to get
|
2007-08-01 05:00:02 +08:00
|
|
|
* the information ethtool needs.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/types.h>
|
2006-01-12 04:17:47 +08:00
|
|
|
#include <linux/capability.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/netdevice.h>
|
2012-04-04 06:59:17 +08:00
|
|
|
#include <linux/net_tstamp.h>
|
|
|
|
#include <linux/phy.h>
|
ethtool: Add direct access to ops->get_sset_count
On 03/04/2010 09:26 AM, Ben Hutchings wrote:
> On Thu, 2010-03-04 at 00:51 -0800, Jeff Kirsher wrote:
>> From: Jeff Garzik<jgarzik@redhat.com>
>>
>> This patch is an alternative approach for accessing string
>> counts, vs. the drvinfo indirect approach. This way the drvinfo
>> space doesn't run out, and we don't break ABI later.
> [...]
>> --- a/net/core/ethtool.c
>> +++ b/net/core/ethtool.c
>> @@ -214,6 +214,10 @@ static noinline int ethtool_get_drvinfo(struct net_device *dev, void __user *use
>> info.cmd = ETHTOOL_GDRVINFO;
>> ops->get_drvinfo(dev,&info);
>>
>> + /*
>> + * this method of obtaining string set info is deprecated;
>> + * consider using ETHTOOL_GSSET_INFO instead
>> + */
>
> This comment belongs on the interface (ethtool.h) not the
> implementation.
Debatable -- the current comment is located at the callsite of
ops->get_sset_count(), which is where an implementor might think to add
a new call. Not all the numeric fields in ethtool_drvinfo are obtained
from ->get_sset_count().
Hence the "some" in the attached patch to include/linux/ethtool.h,
addressing your comment.
> [...]
>> +static noinline int ethtool_get_sset_info(struct net_device *dev,
>> + void __user *useraddr)
>> +{
> [...]
>> + /* calculate size of return buffer */
>> + for (i = 0; i< 64; i++)
>> + if (sset_mask& (1ULL<< i))
>> + n_bits++;
> [...]
>
> We have a function for this:
>
> n_bits = hweight64(sset_mask);
Agreed.
I've attached a follow-up patch, which should enable my/Jeff's kernel
patch to be applied, followed by this one.
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-03-04 16:21:53 +08:00
|
|
|
#include <linux/bitops.h>
|
2010-04-08 12:54:42 +08:00
|
|
|
#include <linux/uaccess.h>
|
2020-01-27 15:20:28 +08:00
|
|
|
#include <linux/vermagic.h>
|
2010-09-22 07:12:11 +08:00
|
|
|
#include <linux/vmalloc.h>
|
2018-03-29 06:44:16 +08:00
|
|
|
#include <linux/sfp.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2011-04-02 07:35:15 +08:00
|
|
|
#include <linux/rtnetlink.h>
|
2017-02-03 02:15:33 +08:00
|
|
|
#include <linux/sched/signal.h>
|
2014-11-16 22:23:05 +08:00
|
|
|
#include <linux/net.h>
|
2019-02-01 02:50:47 +08:00
|
|
|
#include <net/devlink.h>
|
2018-10-01 20:51:36 +08:00
|
|
|
#include <net/xdp_sock.h>
|
2019-02-02 19:50:51 +08:00
|
|
|
#include <net/flow_offload.h>
|
2019-12-27 22:56:03 +08:00
|
|
|
#include <linux/ethtool_netlink.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-12-11 17:58:29 +08:00
|
|
|
#include "common.h"
|
|
|
|
|
2007-02-09 22:24:36 +08:00
|
|
|
/*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Some useful ethtool_ops methods that're device independent.
|
|
|
|
* If we find that all drivers want to do the same thing here,
|
|
|
|
* we can turn these into dev_() function calls.
|
|
|
|
*/
|
|
|
|
|
|
|
|
u32 ethtool_op_get_link(struct net_device *dev)
|
|
|
|
{
|
|
|
|
return netif_carrier_ok(dev) ? 1 : 0;
|
|
|
|
}
|
2010-04-08 12:54:42 +08:00
|
|
|
EXPORT_SYMBOL(ethtool_op_get_link);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-04-04 06:59:22 +08:00
|
|
|
int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
|
|
|
|
{
|
|
|
|
info->so_timestamping =
|
|
|
|
SOF_TIMESTAMPING_TX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_RX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_SOFTWARE;
|
|
|
|
info->phc_index = -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ethtool_op_get_ts_info);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Handlers for each ethtool command */
|
|
|
|
|
2011-02-16 00:59:17 +08:00
|
|
|
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_gfeatures cmd = {
|
|
|
|
.cmd = ETHTOOL_GFEATURES,
|
|
|
|
.size = ETHTOOL_DEV_FEATURE_WORDS,
|
|
|
|
};
|
2011-11-15 23:29:55 +08:00
|
|
|
struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
|
2011-02-16 00:59:17 +08:00
|
|
|
u32 __user *sizeaddr;
|
|
|
|
u32 copy_size;
|
2011-11-15 23:29:55 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* in case feature bits run out again */
|
2011-11-16 22:32:03 +08:00
|
|
|
BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) > sizeof(netdev_features_t));
|
2011-11-15 23:29:55 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {
|
2011-11-16 22:32:03 +08:00
|
|
|
features[i].available = (u32)(dev->hw_features >> (32 * i));
|
|
|
|
features[i].requested = (u32)(dev->wanted_features >> (32 * i));
|
|
|
|
features[i].active = (u32)(dev->features >> (32 * i));
|
|
|
|
features[i].never_changed =
|
|
|
|
(u32)(NETIF_F_NEVER_CHANGE >> (32 * i));
|
2011-11-15 23:29:55 +08:00
|
|
|
}
|
2011-02-16 00:59:17 +08:00
|
|
|
|
|
|
|
sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
|
|
|
|
if (get_user(copy_size, sizeaddr))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (copy_size > ETHTOOL_DEV_FEATURE_WORDS)
|
|
|
|
copy_size = ETHTOOL_DEV_FEATURE_WORDS;
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
|
|
|
|
return -EFAULT;
|
|
|
|
useraddr += sizeof(cmd);
|
|
|
|
if (copy_to_user(useraddr, features, copy_size * sizeof(*features)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_sfeatures cmd;
|
|
|
|
struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
|
2011-11-15 23:29:55 +08:00
|
|
|
netdev_features_t wanted = 0, valid = 0;
|
|
|
|
int i, ret = 0;
|
2011-02-16 00:59:17 +08:00
|
|
|
|
|
|
|
if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
|
|
|
|
return -EFAULT;
|
|
|
|
useraddr += sizeof(cmd);
|
|
|
|
|
|
|
|
if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (copy_from_user(features, useraddr, sizeof(features)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {
|
2011-11-16 22:32:03 +08:00
|
|
|
valid |= (netdev_features_t)features[i].valid << (32 * i);
|
|
|
|
wanted |= (netdev_features_t)features[i].requested << (32 * i);
|
2011-11-15 23:29:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (valid & ~NETIF_F_ETHTOOL_BITS)
|
2011-02-16 00:59:17 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
if (valid & ~dev->hw_features) {
|
|
|
|
valid &= dev->hw_features;
|
2011-02-16 00:59:17 +08:00
|
|
|
ret |= ETHTOOL_F_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
dev->wanted_features &= ~valid;
|
|
|
|
dev->wanted_features |= wanted & valid;
|
2011-04-03 13:48:47 +08:00
|
|
|
__netdev_update_features(dev);
|
2011-02-16 00:59:17 +08:00
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
if ((dev->wanted_features ^ dev->features) & valid)
|
2011-02-16 00:59:17 +08:00
|
|
|
ret |= ETHTOOL_F_WISH;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-02-16 00:59:16 +08:00
|
|
|
static int __ethtool_get_sset_count(struct net_device *dev, int sset)
|
|
|
|
{
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
|
2011-02-16 00:59:17 +08:00
|
|
|
if (sset == ETH_SS_FEATURES)
|
|
|
|
return ARRAY_SIZE(netdev_features_strings);
|
|
|
|
|
2014-12-03 00:12:10 +08:00
|
|
|
if (sset == ETH_SS_RSS_HASH_FUNCS)
|
|
|
|
return ARRAY_SIZE(rss_hash_func_strings);
|
|
|
|
|
2015-06-11 15:28:16 +08:00
|
|
|
if (sset == ETH_SS_TUNABLES)
|
|
|
|
return ARRAY_SIZE(tunable_strings);
|
|
|
|
|
2016-11-17 20:07:21 +08:00
|
|
|
if (sset == ETH_SS_PHY_TUNABLES)
|
|
|
|
return ARRAY_SIZE(phy_tunable_strings);
|
|
|
|
|
2018-04-26 03:12:48 +08:00
|
|
|
if (sset == ETH_SS_PHY_STATS && dev->phydev &&
|
|
|
|
!ops->get_ethtool_phy_stats)
|
|
|
|
return phy_ethtool_get_sset_count(dev->phydev);
|
2015-12-30 23:28:25 +08:00
|
|
|
|
2019-12-11 17:58:34 +08:00
|
|
|
if (sset == ETH_SS_LINK_MODES)
|
|
|
|
return __ETHTOOL_LINK_MODE_MASK_NBITS;
|
|
|
|
|
2013-01-07 17:02:08 +08:00
|
|
|
if (ops->get_sset_count && ops->get_strings)
|
2011-02-16 00:59:16 +08:00
|
|
|
return ops->get_sset_count(dev, sset);
|
|
|
|
else
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __ethtool_get_strings(struct net_device *dev,
|
|
|
|
u32 stringset, u8 *data)
|
|
|
|
{
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
|
2011-02-16 00:59:17 +08:00
|
|
|
if (stringset == ETH_SS_FEATURES)
|
|
|
|
memcpy(data, netdev_features_strings,
|
|
|
|
sizeof(netdev_features_strings));
|
2014-12-03 00:12:10 +08:00
|
|
|
else if (stringset == ETH_SS_RSS_HASH_FUNCS)
|
|
|
|
memcpy(data, rss_hash_func_strings,
|
|
|
|
sizeof(rss_hash_func_strings));
|
2015-06-11 15:28:16 +08:00
|
|
|
else if (stringset == ETH_SS_TUNABLES)
|
|
|
|
memcpy(data, tunable_strings, sizeof(tunable_strings));
|
2016-11-17 20:07:21 +08:00
|
|
|
else if (stringset == ETH_SS_PHY_TUNABLES)
|
|
|
|
memcpy(data, phy_tunable_strings, sizeof(phy_tunable_strings));
|
2018-04-26 03:12:48 +08:00
|
|
|
else if (stringset == ETH_SS_PHY_STATS && dev->phydev &&
|
|
|
|
!ops->get_ethtool_phy_stats)
|
|
|
|
phy_ethtool_get_strings(dev->phydev, data);
|
2019-12-11 17:58:34 +08:00
|
|
|
else if (stringset == ETH_SS_LINK_MODES)
|
|
|
|
memcpy(data, link_mode_names,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS * ETH_GSTRING_LEN);
|
2018-04-26 03:12:48 +08:00
|
|
|
else
|
2011-02-16 00:59:17 +08:00
|
|
|
/* ops->get_strings is valid because checked earlier */
|
|
|
|
ops->get_strings(dev, stringset, data);
|
2011-02-16 00:59:16 +08:00
|
|
|
}
|
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd)
|
2011-02-16 00:59:17 +08:00
|
|
|
{
|
|
|
|
/* feature masks of legacy discrete ethtool ops */
|
|
|
|
|
|
|
|
switch (eth_cmd) {
|
|
|
|
case ETHTOOL_GTXCSUM:
|
|
|
|
case ETHTOOL_STXCSUM:
|
2020-03-13 04:07:43 +08:00
|
|
|
return NETIF_F_CSUM_MASK | NETIF_F_FCOE_CRC_BIT |
|
|
|
|
NETIF_F_SCTP_CRC;
|
2011-02-16 00:59:18 +08:00
|
|
|
case ETHTOOL_GRXCSUM:
|
|
|
|
case ETHTOOL_SRXCSUM:
|
|
|
|
return NETIF_F_RXCSUM;
|
2011-02-16 00:59:17 +08:00
|
|
|
case ETHTOOL_GSG:
|
|
|
|
case ETHTOOL_SSG:
|
2020-03-13 04:07:43 +08:00
|
|
|
return NETIF_F_SG | NETIF_F_FRAGLIST;
|
2011-02-16 00:59:17 +08:00
|
|
|
case ETHTOOL_GTSO:
|
|
|
|
case ETHTOOL_STSO:
|
|
|
|
return NETIF_F_ALL_TSO;
|
|
|
|
case ETHTOOL_GGSO:
|
|
|
|
case ETHTOOL_SGSO:
|
|
|
|
return NETIF_F_GSO;
|
|
|
|
case ETHTOOL_GGRO:
|
|
|
|
case ETHTOOL_SGRO:
|
|
|
|
return NETIF_F_GRO;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_get_one_feature(struct net_device *dev,
|
|
|
|
char __user *useraddr, u32 ethcmd)
|
|
|
|
{
|
2011-11-15 23:29:55 +08:00
|
|
|
netdev_features_t mask = ethtool_get_feature_mask(ethcmd);
|
2011-02-16 00:59:17 +08:00
|
|
|
struct ethtool_value edata = {
|
|
|
|
.cmd = ethcmd,
|
2011-02-16 00:59:17 +08:00
|
|
|
.data = !!(dev->features & mask),
|
2011-02-16 00:59:17 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &edata, sizeof(edata)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_set_one_feature(struct net_device *dev,
|
|
|
|
void __user *useraddr, u32 ethcmd)
|
|
|
|
{
|
|
|
|
struct ethtool_value edata;
|
2011-11-15 23:29:55 +08:00
|
|
|
netdev_features_t mask;
|
2011-02-16 00:59:17 +08:00
|
|
|
|
|
|
|
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2011-02-16 00:59:17 +08:00
|
|
|
mask = ethtool_get_feature_mask(ethcmd);
|
|
|
|
mask &= dev->hw_features;
|
2011-11-15 23:29:55 +08:00
|
|
|
if (!mask)
|
|
|
|
return -EOPNOTSUPP;
|
2011-02-16 00:59:17 +08:00
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
if (edata.data)
|
|
|
|
dev->wanted_features |= mask;
|
|
|
|
else
|
|
|
|
dev->wanted_features &= ~mask;
|
2011-02-16 00:59:17 +08:00
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
__netdev_update_features(dev);
|
2011-02-16 00:59:17 +08:00
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
#define ETH_ALL_FLAGS (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | \
|
|
|
|
ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH)
|
2013-04-19 10:04:27 +08:00
|
|
|
#define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_CTAG_RX | \
|
|
|
|
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_NTUPLE | \
|
|
|
|
NETIF_F_RXHASH)
|
2011-11-15 23:29:55 +08:00
|
|
|
|
|
|
|
static u32 __ethtool_get_flags(struct net_device *dev)
|
|
|
|
{
|
2011-11-15 23:29:55 +08:00
|
|
|
u32 flags = 0;
|
|
|
|
|
2013-07-13 21:43:00 +08:00
|
|
|
if (dev->features & NETIF_F_LRO)
|
|
|
|
flags |= ETH_FLAG_LRO;
|
|
|
|
if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
|
|
|
|
flags |= ETH_FLAG_RXVLAN;
|
|
|
|
if (dev->features & NETIF_F_HW_VLAN_CTAG_TX)
|
|
|
|
flags |= ETH_FLAG_TXVLAN;
|
|
|
|
if (dev->features & NETIF_F_NTUPLE)
|
|
|
|
flags |= ETH_FLAG_NTUPLE;
|
|
|
|
if (dev->features & NETIF_F_RXHASH)
|
|
|
|
flags |= ETH_FLAG_RXHASH;
|
2011-11-15 23:29:55 +08:00
|
|
|
|
|
|
|
return flags;
|
2011-02-16 00:59:17 +08:00
|
|
|
}
|
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
static int __ethtool_set_flags(struct net_device *dev, u32 data)
|
2011-02-16 00:59:18 +08:00
|
|
|
{
|
2011-11-15 23:29:55 +08:00
|
|
|
netdev_features_t features = 0, changed;
|
2011-02-16 00:59:18 +08:00
|
|
|
|
2011-11-15 23:29:55 +08:00
|
|
|
if (data & ~ETH_ALL_FLAGS)
|
2011-02-16 00:59:18 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-07-13 21:43:00 +08:00
|
|
|
if (data & ETH_FLAG_LRO)
|
|
|
|
features |= NETIF_F_LRO;
|
|
|
|
if (data & ETH_FLAG_RXVLAN)
|
|
|
|
features |= NETIF_F_HW_VLAN_CTAG_RX;
|
|
|
|
if (data & ETH_FLAG_TXVLAN)
|
|
|
|
features |= NETIF_F_HW_VLAN_CTAG_TX;
|
|
|
|
if (data & ETH_FLAG_NTUPLE)
|
|
|
|
features |= NETIF_F_NTUPLE;
|
|
|
|
if (data & ETH_FLAG_RXHASH)
|
|
|
|
features |= NETIF_F_RXHASH;
|
2011-11-15 23:29:55 +08:00
|
|
|
|
2011-02-16 00:59:18 +08:00
|
|
|
/* allow changing only bits set in hw_features */
|
2011-11-15 23:29:55 +08:00
|
|
|
changed = (features ^ dev->features) & ETH_ALL_FEATURES;
|
2011-02-16 00:59:18 +08:00
|
|
|
if (changed & ~dev->hw_features)
|
|
|
|
return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP;
|
|
|
|
|
|
|
|
dev->wanted_features =
|
2011-11-15 23:29:55 +08:00
|
|
|
(dev->wanted_features & ~changed) | (features & changed);
|
2011-02-16 00:59:18 +08:00
|
|
|
|
2011-04-03 13:48:47 +08:00
|
|
|
__netdev_update_features(dev);
|
2011-02-16 00:59:18 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-06 05:53:40 +08:00
|
|
|
/* Given two link masks, AND them together and save the result in dst. */
|
|
|
|
void ethtool_intersect_link_masks(struct ethtool_link_ksettings *dst,
|
|
|
|
struct ethtool_link_ksettings *src)
|
|
|
|
{
|
|
|
|
unsigned int size = BITS_TO_LONGS(__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
unsigned int idx = 0;
|
|
|
|
|
|
|
|
for (; idx < size; idx++) {
|
|
|
|
dst->link_modes.supported[idx] &=
|
|
|
|
src->link_modes.supported[idx];
|
|
|
|
dst->link_modes.advertising[idx] &=
|
|
|
|
src->link_modes.advertising[idx];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ethtool_intersect_link_masks);
|
|
|
|
|
2016-04-15 06:34:59 +08:00
|
|
|
void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst,
|
|
|
|
u32 legacy_u32)
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
{
|
|
|
|
bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
dst[0] = legacy_u32;
|
|
|
|
}
|
2016-04-15 06:34:59 +08:00
|
|
|
EXPORT_SYMBOL(ethtool_convert_legacy_u32_to_link_mode);
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
|
|
|
|
/* return false if src had higher bits set. lower bits always updated. */
|
2016-04-15 06:34:59 +08:00
|
|
|
bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
|
|
|
|
const unsigned long *src)
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
{
|
|
|
|
bool retval = true;
|
|
|
|
|
|
|
|
/* TODO: following test will soon always be true */
|
|
|
|
if (__ETHTOOL_LINK_MODE_MASK_NBITS > 32) {
|
|
|
|
__ETHTOOL_DECLARE_LINK_MODE_MASK(ext);
|
|
|
|
|
|
|
|
bitmap_zero(ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
bitmap_fill(ext, 32);
|
|
|
|
bitmap_complement(ext, ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
if (bitmap_intersects(ext, src,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS)) {
|
|
|
|
/* src mask goes beyond bit 31 */
|
|
|
|
retval = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*legacy_u32 = src[0];
|
|
|
|
return retval;
|
|
|
|
}
|
2016-04-15 06:34:59 +08:00
|
|
|
EXPORT_SYMBOL(ethtool_convert_link_mode_to_legacy_u32);
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
|
|
|
|
/* return false if ksettings link modes had higher bits
|
|
|
|
* set. legacy_settings always updated (best effort)
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
convert_link_ksettings_to_legacy_settings(
|
|
|
|
struct ethtool_cmd *legacy_settings,
|
|
|
|
const struct ethtool_link_ksettings *link_ksettings)
|
|
|
|
{
|
|
|
|
bool retval = true;
|
|
|
|
|
|
|
|
memset(legacy_settings, 0, sizeof(*legacy_settings));
|
|
|
|
/* this also clears the deprecated fields in legacy structure:
|
|
|
|
* __u8 transceiver;
|
|
|
|
* __u32 maxtxpkt;
|
|
|
|
* __u32 maxrxpkt;
|
|
|
|
*/
|
|
|
|
|
2016-04-15 06:34:59 +08:00
|
|
|
retval &= ethtool_convert_link_mode_to_legacy_u32(
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
&legacy_settings->supported,
|
|
|
|
link_ksettings->link_modes.supported);
|
2016-04-15 06:34:59 +08:00
|
|
|
retval &= ethtool_convert_link_mode_to_legacy_u32(
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
&legacy_settings->advertising,
|
|
|
|
link_ksettings->link_modes.advertising);
|
2016-04-15 06:34:59 +08:00
|
|
|
retval &= ethtool_convert_link_mode_to_legacy_u32(
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
&legacy_settings->lp_advertising,
|
|
|
|
link_ksettings->link_modes.lp_advertising);
|
|
|
|
ethtool_cmd_speed_set(legacy_settings, link_ksettings->base.speed);
|
|
|
|
legacy_settings->duplex
|
|
|
|
= link_ksettings->base.duplex;
|
|
|
|
legacy_settings->port
|
|
|
|
= link_ksettings->base.port;
|
|
|
|
legacy_settings->phy_address
|
|
|
|
= link_ksettings->base.phy_address;
|
|
|
|
legacy_settings->autoneg
|
|
|
|
= link_ksettings->base.autoneg;
|
|
|
|
legacy_settings->mdio_support
|
|
|
|
= link_ksettings->base.mdio_support;
|
|
|
|
legacy_settings->eth_tp_mdix
|
|
|
|
= link_ksettings->base.eth_tp_mdix;
|
|
|
|
legacy_settings->eth_tp_mdix_ctrl
|
|
|
|
= link_ksettings->base.eth_tp_mdix_ctrl;
|
2017-09-21 06:52:13 +08:00
|
|
|
legacy_settings->transceiver
|
|
|
|
= link_ksettings->base.transceiver;
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* number of 32-bit words to store the user's link mode bitmaps */
|
|
|
|
#define __ETHTOOL_LINK_MODE_MASK_NU32 \
|
|
|
|
DIV_ROUND_UP(__ETHTOOL_LINK_MODE_MASK_NBITS, 32)
|
|
|
|
|
|
|
|
/* layout of the struct passed from/to userland */
|
|
|
|
struct ethtool_link_usettings {
|
|
|
|
struct ethtool_link_settings base;
|
|
|
|
struct {
|
|
|
|
__u32 supported[__ETHTOOL_LINK_MODE_MASK_NU32];
|
|
|
|
__u32 advertising[__ETHTOOL_LINK_MODE_MASK_NU32];
|
|
|
|
__u32 lp_advertising[__ETHTOOL_LINK_MODE_MASK_NU32];
|
|
|
|
} link_modes;
|
|
|
|
};
|
|
|
|
|
2018-08-29 01:56:58 +08:00
|
|
|
/* Internal kernel helper to query a device ethtool_link_settings. */
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
int __ethtool_get_link_ksettings(struct net_device *dev,
|
|
|
|
struct ethtool_link_ksettings *link_ksettings)
|
|
|
|
{
|
|
|
|
ASSERT_RTNL();
|
|
|
|
|
2018-08-29 01:56:58 +08:00
|
|
|
if (!dev->ethtool_ops->get_link_ksettings)
|
2016-02-25 02:58:11 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2018-08-29 01:56:58 +08:00
|
|
|
memset(link_ksettings, 0, sizeof(*link_ksettings));
|
|
|
|
return dev->ethtool_ops->get_link_ksettings(dev, link_ksettings);
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__ethtool_get_link_ksettings);
|
|
|
|
|
|
|
|
/* convert ethtool_link_usettings in user space to a kernel internal
|
|
|
|
* ethtool_link_ksettings. return 0 on success, errno on error.
|
|
|
|
*/
|
|
|
|
static int load_link_ksettings_from_user(struct ethtool_link_ksettings *to,
|
|
|
|
const void __user *from)
|
|
|
|
{
|
|
|
|
struct ethtool_link_usettings link_usettings;
|
|
|
|
|
|
|
|
if (copy_from_user(&link_usettings, from, sizeof(link_usettings)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
memcpy(&to->base, &link_usettings.base, sizeof(to->base));
|
2018-02-07 07:38:06 +08:00
|
|
|
bitmap_from_arr32(to->link_modes.supported,
|
|
|
|
link_usettings.link_modes.supported,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
bitmap_from_arr32(to->link_modes.advertising,
|
|
|
|
link_usettings.link_modes.advertising,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
bitmap_from_arr32(to->link_modes.lp_advertising,
|
|
|
|
link_usettings.link_modes.lp_advertising,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-29 04:12:04 +08:00
|
|
|
/* Check if the user is trying to change anything besides speed/duplex */
|
|
|
|
bool ethtool_virtdev_validate_cmd(const struct ethtool_link_ksettings *cmd)
|
|
|
|
{
|
|
|
|
struct ethtool_link_settings base2 = {};
|
|
|
|
|
|
|
|
base2.speed = cmd->base.speed;
|
|
|
|
base2.port = PORT_OTHER;
|
|
|
|
base2.duplex = cmd->base.duplex;
|
|
|
|
base2.cmd = cmd->base.cmd;
|
|
|
|
base2.link_mode_masks_nwords = cmd->base.link_mode_masks_nwords;
|
|
|
|
|
|
|
|
return !memcmp(&base2, &cmd->base, sizeof(base2)) &&
|
|
|
|
bitmap_empty(cmd->link_modes.supported,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS) &&
|
|
|
|
bitmap_empty(cmd->link_modes.lp_advertising,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
}
|
|
|
|
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
/* convert a kernel internal ethtool_link_ksettings to
|
|
|
|
* ethtool_link_usettings in user space. return 0 on success, errno on
|
|
|
|
* error.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
store_link_ksettings_for_user(void __user *to,
|
|
|
|
const struct ethtool_link_ksettings *from)
|
|
|
|
{
|
|
|
|
struct ethtool_link_usettings link_usettings;
|
|
|
|
|
|
|
|
memcpy(&link_usettings.base, &from->base, sizeof(link_usettings));
|
2018-02-07 07:38:06 +08:00
|
|
|
bitmap_to_arr32(link_usettings.link_modes.supported,
|
|
|
|
from->link_modes.supported,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
bitmap_to_arr32(link_usettings.link_modes.advertising,
|
|
|
|
from->link_modes.advertising,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
|
bitmap_to_arr32(link_usettings.link_modes.lp_advertising,
|
|
|
|
from->link_modes.lp_advertising,
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
|
|
|
|
if (copy_to_user(to, &link_usettings, sizeof(link_usettings)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-29 01:56:58 +08:00
|
|
|
/* Query device for its ethtool_link_settings. */
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
static int ethtool_get_link_ksettings(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct ethtool_link_ksettings link_ksettings;
|
|
|
|
|
|
|
|
ASSERT_RTNL();
|
|
|
|
if (!dev->ethtool_ops->get_link_ksettings)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
/* handle bitmap nbits handshake */
|
|
|
|
if (copy_from_user(&link_ksettings.base, useraddr,
|
|
|
|
sizeof(link_ksettings.base)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (__ETHTOOL_LINK_MODE_MASK_NU32
|
|
|
|
!= link_ksettings.base.link_mode_masks_nwords) {
|
|
|
|
/* wrong link mode nbits requested */
|
|
|
|
memset(&link_ksettings, 0, sizeof(link_ksettings));
|
2016-03-14 09:05:38 +08:00
|
|
|
link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
/* send back number of words required as negative val */
|
|
|
|
compiletime_assert(__ETHTOOL_LINK_MODE_MASK_NU32 <= S8_MAX,
|
|
|
|
"need too many bits for link modes!");
|
|
|
|
link_ksettings.base.link_mode_masks_nwords
|
|
|
|
= -((s8)__ETHTOOL_LINK_MODE_MASK_NU32);
|
|
|
|
|
|
|
|
/* copy the base fields back to user, not the link
|
|
|
|
* mode bitmaps
|
|
|
|
*/
|
|
|
|
if (copy_to_user(useraddr, &link_ksettings.base,
|
|
|
|
sizeof(link_ksettings.base)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handshake successful: user/kernel agree on
|
|
|
|
* link_mode_masks_nwords
|
|
|
|
*/
|
|
|
|
|
|
|
|
memset(&link_ksettings, 0, sizeof(link_ksettings));
|
|
|
|
err = dev->ethtool_ops->get_link_ksettings(dev, &link_ksettings);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* make sure we tell the right values to user */
|
|
|
|
link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
|
|
|
|
link_ksettings.base.link_mode_masks_nwords
|
|
|
|
= __ETHTOOL_LINK_MODE_MASK_NU32;
|
|
|
|
|
|
|
|
return store_link_ksettings_for_user(useraddr, &link_ksettings);
|
|
|
|
}
|
|
|
|
|
2018-08-29 01:56:58 +08:00
|
|
|
/* Update device ethtool_link_settings. */
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
static int ethtool_set_link_ksettings(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct ethtool_link_ksettings link_ksettings;
|
|
|
|
|
|
|
|
ASSERT_RTNL();
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->set_link_ksettings)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
/* make sure nbits field has expected value */
|
|
|
|
if (copy_from_user(&link_ksettings.base, useraddr,
|
|
|
|
sizeof(link_ksettings.base)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (__ETHTOOL_LINK_MODE_MASK_NU32
|
|
|
|
!= link_ksettings.base.link_mode_masks_nwords)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* copy the whole structure, now that we know it has expected
|
|
|
|
* format
|
|
|
|
*/
|
|
|
|
err = load_link_ksettings_from_user(&link_ksettings, useraddr);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* re-check nwords field, just in case */
|
|
|
|
if (__ETHTOOL_LINK_MODE_MASK_NU32
|
|
|
|
!= link_ksettings.base.link_mode_masks_nwords)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-12-27 22:56:03 +08:00
|
|
|
err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
|
2019-12-27 22:56:18 +08:00
|
|
|
if (err >= 0) {
|
2019-12-27 22:56:03 +08:00
|
|
|
ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
|
2019-12-27 22:56:18 +08:00
|
|
|
ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL);
|
|
|
|
}
|
2019-12-27 22:56:03 +08:00
|
|
|
return err;
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
}
|
|
|
|
|
2020-02-29 04:12:04 +08:00
|
|
|
int ethtool_virtdev_set_link_ksettings(struct net_device *dev,
|
|
|
|
const struct ethtool_link_ksettings *cmd,
|
|
|
|
u32 *dev_speed, u8 *dev_duplex)
|
|
|
|
{
|
|
|
|
u32 speed;
|
|
|
|
u8 duplex;
|
|
|
|
|
|
|
|
speed = cmd->base.speed;
|
|
|
|
duplex = cmd->base.duplex;
|
|
|
|
/* don't allow custom speed and duplex */
|
|
|
|
if (!ethtool_validate_speed(speed) ||
|
|
|
|
!ethtool_validate_duplex(duplex) ||
|
|
|
|
!ethtool_virtdev_validate_cmd(cmd))
|
|
|
|
return -EINVAL;
|
|
|
|
*dev_speed = speed;
|
|
|
|
*dev_duplex = duplex;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ethtool_virtdev_set_link_ksettings);
|
|
|
|
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
/* Query device for its ethtool_cmd settings.
|
|
|
|
*
|
2018-08-29 01:56:58 +08:00
|
|
|
* Backward compatibility note: for compatibility with legacy ethtool, this is
|
|
|
|
* now implemented via get_link_ksettings. When driver reports higher link mode
|
|
|
|
* bits, a kernel warning is logged once (with name of 1st driver/device) to
|
|
|
|
* recommend user to upgrade ethtool, but the command is successful (only the
|
|
|
|
* lower link mode bits reported back to user). Deprecated fields from
|
|
|
|
* ethtool_cmd (transceiver/maxrxpkt/maxtxpkt) are always set to zero.
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
*/
|
2011-09-03 11:34:30 +08:00
|
|
|
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
2018-08-29 01:56:58 +08:00
|
|
|
struct ethtool_link_ksettings link_ksettings;
|
2011-09-03 11:34:30 +08:00
|
|
|
struct ethtool_cmd cmd;
|
2018-08-29 01:56:58 +08:00
|
|
|
int err;
|
2011-09-03 11:34:30 +08:00
|
|
|
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
ASSERT_RTNL();
|
2018-08-29 01:56:58 +08:00
|
|
|
if (!dev->ethtool_ops->get_link_ksettings)
|
|
|
|
return -EOPNOTSUPP;
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
|
2018-08-29 01:56:58 +08:00
|
|
|
memset(&link_ksettings, 0, sizeof(link_ksettings));
|
|
|
|
err = dev->ethtool_ops->get_link_ksettings(dev, &link_ksettings);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
convert_link_ksettings_to_legacy_settings(&cmd, &link_ksettings);
|
2016-02-25 02:58:11 +08:00
|
|
|
|
2018-08-29 01:56:58 +08:00
|
|
|
/* send a sensible cmd tag back to user */
|
|
|
|
cmd.cmd = ETHTOOL_GSET;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
|
|
|
|
return -EFAULT;
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
/* Update device link settings with given ethtool_cmd.
|
|
|
|
*
|
2018-08-29 01:56:58 +08:00
|
|
|
* Backward compatibility note: for compatibility with legacy ethtool, this is
|
|
|
|
* now always implemented via set_link_settings. When user's request updates
|
|
|
|
* deprecated ethtool_cmd fields (transceiver/maxrxpkt/maxtxpkt), a kernel
|
|
|
|
* warning is logged once (with name of 1st driver/device) to recommend user to
|
|
|
|
* upgrade ethtool, and the request is rejected.
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
2018-08-29 01:56:58 +08:00
|
|
|
struct ethtool_link_ksettings link_ksettings;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ethtool_cmd cmd;
|
2019-12-27 22:56:03 +08:00
|
|
|
int ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
ASSERT_RTNL();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
|
|
|
|
return -EFAULT;
|
2018-08-29 01:56:58 +08:00
|
|
|
if (!dev->ethtool_ops->set_link_ksettings)
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2018-08-29 01:56:58 +08:00
|
|
|
if (!convert_legacy_settings_to_link_ksettings(&link_ksettings, &cmd))
|
|
|
|
return -EINVAL;
|
|
|
|
link_ksettings.base.link_mode_masks_nwords =
|
|
|
|
__ETHTOOL_LINK_MODE_MASK_NU32;
|
2019-12-27 22:56:03 +08:00
|
|
|
ret = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
|
2019-12-27 22:56:18 +08:00
|
|
|
if (ret >= 0) {
|
2019-12-27 22:56:03 +08:00
|
|
|
ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
|
2019-12-27 22:56:18 +08:00
|
|
|
ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL);
|
|
|
|
}
|
2019-12-27 22:56:03 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-04-08 12:54:42 +08:00
|
|
|
static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ethtool_drvinfo info;
|
2006-09-09 02:16:13 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.cmd = ETHTOOL_GDRVINFO;
|
2020-01-27 15:20:28 +08:00
|
|
|
strlcpy(info.version, UTS_RELEASE, sizeof(info.version));
|
2013-01-07 17:02:08 +08:00
|
|
|
if (ops->get_drvinfo) {
|
2010-08-17 17:31:15 +08:00
|
|
|
ops->get_drvinfo(dev, &info);
|
|
|
|
} else if (dev->dev.parent && dev->dev.parent->driver) {
|
|
|
|
strlcpy(info.bus_info, dev_name(dev->dev.parent),
|
|
|
|
sizeof(info.bus_info));
|
|
|
|
strlcpy(info.driver, dev->dev.parent->driver->name,
|
|
|
|
sizeof(info.driver));
|
|
|
|
} else {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-03-04 06:51:50 +08:00
|
|
|
/*
|
|
|
|
* this method of obtaining string set info is deprecated;
|
ethtool: Add direct access to ops->get_sset_count
On 03/04/2010 09:26 AM, Ben Hutchings wrote:
> On Thu, 2010-03-04 at 00:51 -0800, Jeff Kirsher wrote:
>> From: Jeff Garzik<jgarzik@redhat.com>
>>
>> This patch is an alternative approach for accessing string
>> counts, vs. the drvinfo indirect approach. This way the drvinfo
>> space doesn't run out, and we don't break ABI later.
> [...]
>> --- a/net/core/ethtool.c
>> +++ b/net/core/ethtool.c
>> @@ -214,6 +214,10 @@ static noinline int ethtool_get_drvinfo(struct net_device *dev, void __user *use
>> info.cmd = ETHTOOL_GDRVINFO;
>> ops->get_drvinfo(dev,&info);
>>
>> + /*
>> + * this method of obtaining string set info is deprecated;
>> + * consider using ETHTOOL_GSSET_INFO instead
>> + */
>
> This comment belongs on the interface (ethtool.h) not the
> implementation.
Debatable -- the current comment is located at the callsite of
ops->get_sset_count(), which is where an implementor might think to add
a new call. Not all the numeric fields in ethtool_drvinfo are obtained
from ->get_sset_count().
Hence the "some" in the attached patch to include/linux/ethtool.h,
addressing your comment.
> [...]
>> +static noinline int ethtool_get_sset_info(struct net_device *dev,
>> + void __user *useraddr)
>> +{
> [...]
>> + /* calculate size of return buffer */
>> + for (i = 0; i< 64; i++)
>> + if (sset_mask& (1ULL<< i))
>> + n_bits++;
> [...]
>
> We have a function for this:
>
> n_bits = hweight64(sset_mask);
Agreed.
I've attached a follow-up patch, which should enable my/Jeff's kernel
patch to be applied, followed by this one.
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-03-04 16:21:53 +08:00
|
|
|
* Use ETHTOOL_GSSET_INFO instead.
|
2010-03-04 06:51:50 +08:00
|
|
|
*/
|
2013-01-07 17:02:08 +08:00
|
|
|
if (ops->get_sset_count) {
|
2007-08-16 07:01:08 +08:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = ops->get_sset_count(dev, ETH_SS_TEST);
|
|
|
|
if (rc >= 0)
|
|
|
|
info.testinfo_len = rc;
|
|
|
|
rc = ops->get_sset_count(dev, ETH_SS_STATS);
|
|
|
|
if (rc >= 0)
|
|
|
|
info.n_stats = rc;
|
2007-08-16 07:01:32 +08:00
|
|
|
rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
|
|
|
|
if (rc >= 0)
|
|
|
|
info.n_priv_flags = rc;
|
2007-08-16 07:01:08 +08:00
|
|
|
}
|
2018-12-26 19:51:46 +08:00
|
|
|
if (ops->get_regs_len) {
|
|
|
|
int ret = ops->get_regs_len(dev);
|
|
|
|
|
|
|
|
if (ret > 0)
|
|
|
|
info.regdump_len = ret;
|
|
|
|
}
|
|
|
|
|
2013-01-07 17:02:08 +08:00
|
|
|
if (ops->get_eeprom_len)
|
2005-04-17 06:20:36 +08:00
|
|
|
info.eedump_len = ops->get_eeprom_len(dev);
|
|
|
|
|
2019-02-01 02:50:47 +08:00
|
|
|
if (!info.fw_version[0])
|
|
|
|
devlink_compat_running_version(dev, info.fw_version,
|
|
|
|
sizeof(info.fw_version));
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copy_to_user(useraddr, &info, sizeof(info)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-09 04:17:04 +08:00
|
|
|
static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
|
2010-04-08 12:54:42 +08:00
|
|
|
void __user *useraddr)
|
2010-03-04 06:51:50 +08:00
|
|
|
{
|
|
|
|
struct ethtool_sset_info info;
|
|
|
|
u64 sset_mask;
|
|
|
|
int i, idx = 0, n_bits = 0, ret, rc;
|
|
|
|
u32 *info_buf = NULL;
|
|
|
|
|
|
|
|
if (copy_from_user(&info, useraddr, sizeof(info)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* store copy of mask, because we zero struct later on */
|
|
|
|
sset_mask = info.sset_mask;
|
|
|
|
if (!sset_mask)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* calculate size of return buffer */
|
ethtool: Add direct access to ops->get_sset_count
On 03/04/2010 09:26 AM, Ben Hutchings wrote:
> On Thu, 2010-03-04 at 00:51 -0800, Jeff Kirsher wrote:
>> From: Jeff Garzik<jgarzik@redhat.com>
>>
>> This patch is an alternative approach for accessing string
>> counts, vs. the drvinfo indirect approach. This way the drvinfo
>> space doesn't run out, and we don't break ABI later.
> [...]
>> --- a/net/core/ethtool.c
>> +++ b/net/core/ethtool.c
>> @@ -214,6 +214,10 @@ static noinline int ethtool_get_drvinfo(struct net_device *dev, void __user *use
>> info.cmd = ETHTOOL_GDRVINFO;
>> ops->get_drvinfo(dev,&info);
>>
>> + /*
>> + * this method of obtaining string set info is deprecated;
>> + * consider using ETHTOOL_GSSET_INFO instead
>> + */
>
> This comment belongs on the interface (ethtool.h) not the
> implementation.
Debatable -- the current comment is located at the callsite of
ops->get_sset_count(), which is where an implementor might think to add
a new call. Not all the numeric fields in ethtool_drvinfo are obtained
from ->get_sset_count().
Hence the "some" in the attached patch to include/linux/ethtool.h,
addressing your comment.
> [...]
>> +static noinline int ethtool_get_sset_info(struct net_device *dev,
>> + void __user *useraddr)
>> +{
> [...]
>> + /* calculate size of return buffer */
>> + for (i = 0; i< 64; i++)
>> + if (sset_mask& (1ULL<< i))
>> + n_bits++;
> [...]
>
> We have a function for this:
>
> n_bits = hweight64(sset_mask);
Agreed.
I've attached a follow-up patch, which should enable my/Jeff's kernel
patch to be applied, followed by this one.
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-03-04 16:21:53 +08:00
|
|
|
n_bits = hweight64(sset_mask);
|
2010-03-04 06:51:50 +08:00
|
|
|
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.cmd = ETHTOOL_GSSET_INFO;
|
|
|
|
|
treewide: kzalloc() -> kcalloc()
The kzalloc() function has a 2-factor argument form, kcalloc(). This
patch replaces cases of:
kzalloc(a * b, gfp)
with:
kcalloc(a * b, gfp)
as well as handling cases of:
kzalloc(a * b * c, gfp)
with:
kzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kzalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kzalloc
+ kcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kzalloc(sizeof(THING) * C2, ...)
|
kzalloc(sizeof(TYPE) * C2, ...)
|
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(C1 * C2, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 05:03:40 +08:00
|
|
|
info_buf = kcalloc(n_bits, sizeof(u32), GFP_USER);
|
2010-03-04 06:51:50 +08:00
|
|
|
if (!info_buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fill return buffer based on input bitmask and successful
|
|
|
|
* get_sset_count return
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
if (!(sset_mask & (1ULL << i)))
|
|
|
|
continue;
|
|
|
|
|
2011-02-16 00:59:16 +08:00
|
|
|
rc = __ethtool_get_sset_count(dev, i);
|
2010-03-04 06:51:50 +08:00
|
|
|
if (rc >= 0) {
|
|
|
|
info.sset_mask |= (1ULL << i);
|
|
|
|
info_buf[idx++] = rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(useraddr, &info, sizeof(info)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
useraddr += offsetof(struct ethtool_sset_info, data);
|
|
|
|
if (copy_to_user(useraddr, info_buf, idx * sizeof(u32)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(info_buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-08 12:54:42 +08:00
|
|
|
static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
|
2010-06-28 16:45:58 +08:00
|
|
|
u32 cmd, void __user *useraddr)
|
2008-07-02 18:47:41 +08:00
|
|
|
{
|
2010-06-28 16:45:58 +08:00
|
|
|
struct ethtool_rxnfc info;
|
|
|
|
size_t info_size = sizeof(info);
|
2012-01-03 20:04:51 +08:00
|
|
|
int rc;
|
2008-07-02 18:47:41 +08:00
|
|
|
|
2009-02-20 16:58:13 +08:00
|
|
|
if (!dev->ethtool_ops->set_rxnfc)
|
2008-07-02 18:47:41 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2010-06-28 16:45:58 +08:00
|
|
|
/* struct ethtool_rxnfc was originally defined for
|
|
|
|
* ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
|
|
|
|
* members. User-space might still be using that
|
|
|
|
* definition. */
|
|
|
|
if (cmd == ETHTOOL_SRXFH)
|
|
|
|
info_size = (offsetof(struct ethtool_rxnfc, data) +
|
|
|
|
sizeof(info.data));
|
|
|
|
|
|
|
|
if (copy_from_user(&info, useraddr, info_size))
|
2008-07-02 18:47:41 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2012-01-03 20:04:51 +08:00
|
|
|
rc = dev->ethtool_ops->set_rxnfc(dev, &info);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (cmd == ETHTOOL_SRXCLSRLINS &&
|
|
|
|
copy_to_user(useraddr, &info, info_size))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
2008-07-02 18:47:41 +08:00
|
|
|
}
|
|
|
|
|
2010-04-08 12:54:42 +08:00
|
|
|
static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
|
2010-06-28 16:45:58 +08:00
|
|
|
u32 cmd, void __user *useraddr)
|
2008-07-02 18:47:41 +08:00
|
|
|
{
|
|
|
|
struct ethtool_rxnfc info;
|
2010-06-28 16:45:58 +08:00
|
|
|
size_t info_size = sizeof(info);
|
2009-02-20 16:58:13 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
int ret;
|
|
|
|
void *rule_buf = NULL;
|
2008-07-02 18:47:41 +08:00
|
|
|
|
2009-02-20 16:58:13 +08:00
|
|
|
if (!ops->get_rxnfc)
|
2008-07-02 18:47:41 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2010-06-28 16:45:58 +08:00
|
|
|
/* struct ethtool_rxnfc was originally defined for
|
|
|
|
* ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
|
|
|
|
* members. User-space might still be using that
|
|
|
|
* definition. */
|
|
|
|
if (cmd == ETHTOOL_GRXFH)
|
|
|
|
info_size = (offsetof(struct ethtool_rxnfc, data) +
|
|
|
|
sizeof(info.data));
|
|
|
|
|
|
|
|
if (copy_from_user(&info, useraddr, info_size))
|
2008-07-02 18:47:41 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2018-03-08 23:45:03 +08:00
|
|
|
/* If FLOW_RSS was requested then user-space must be using the
|
|
|
|
* new definition, as FLOW_RSS is newer.
|
|
|
|
*/
|
|
|
|
if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) {
|
|
|
|
info_size = sizeof(info);
|
|
|
|
if (copy_from_user(&info, useraddr, info_size))
|
|
|
|
return -EFAULT;
|
ethtool: fix a potential missing-check bug
In ethtool_get_rxnfc(), the object "info" is firstly copied from
user-space. If the FLOW_RSS flag is set in the member field flow_type of
"info" (and cmd is ETHTOOL_GRXFH), info needs to be copied again from
user-space because FLOW_RSS is newer and has new definition, as mentioned
in the comment. However, given that the user data resides in user-space, a
malicious user can race to change the data after the first copy. By doing
so, the user can inject inconsistent data. For example, in the second
copy, the FLOW_RSS flag could be cleared in the field flow_type of "info".
In the following execution, "info" will be used in the function
ops->get_rxnfc(). Such inconsistent data can potentially lead to unexpected
information leakage since ops->get_rxnfc() will prepare various types of
data according to flow_type, and the prepared data will be eventually
copied to user-space. This inconsistent data may also cause undefined
behaviors based on how ops->get_rxnfc() is implemented.
This patch simply re-verifies the flow_type field of "info" after the
second copy. If the value is not as expected, an error code will be
returned.
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-05-01 01:31:13 +08:00
|
|
|
/* Since malicious users may modify the original data,
|
|
|
|
* we need to check whether FLOW_RSS is still requested.
|
|
|
|
*/
|
|
|
|
if (!(info.flow_type & FLOW_RSS))
|
|
|
|
return -EINVAL;
|
2018-03-08 23:45:03 +08:00
|
|
|
}
|
|
|
|
|
2018-10-09 21:15:38 +08:00
|
|
|
if (info.cmd != cmd)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-02-20 16:58:13 +08:00
|
|
|
if (info.cmd == ETHTOOL_GRXCLSRLALL) {
|
|
|
|
if (info.rule_cnt > 0) {
|
2010-06-28 16:44:07 +08:00
|
|
|
if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
|
treewide: kzalloc() -> kcalloc()
The kzalloc() function has a 2-factor argument form, kcalloc(). This
patch replaces cases of:
kzalloc(a * b, gfp)
with:
kcalloc(a * b, gfp)
as well as handling cases of:
kzalloc(a * b * c, gfp)
with:
kzalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kzalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kzalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kzalloc
+ kcalloc
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kzalloc(sizeof(THING) * C2, ...)
|
kzalloc(sizeof(TYPE) * C2, ...)
|
kzalloc(C1 * C2 * C3, ...)
|
kzalloc(C1 * C2, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * E2
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kzalloc
+ kcalloc
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 05:03:40 +08:00
|
|
|
rule_buf = kcalloc(info.rule_cnt, sizeof(u32),
|
2010-06-28 16:44:07 +08:00
|
|
|
GFP_USER);
|
2009-02-20 16:58:13 +08:00
|
|
|
if (!rule_buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
2008-07-02 18:47:41 +08:00
|
|
|
|
2009-02-20 16:58:13 +08:00
|
|
|
ret = ops->get_rxnfc(dev, &info, rule_buf);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err_out;
|
|
|
|
|
|
|
|
ret = -EFAULT;
|
2010-06-28 16:45:58 +08:00
|
|
|
if (copy_to_user(useraddr, &info, info_size))
|
2009-02-20 16:58:13 +08:00
|
|
|
goto err_out;
|
|
|
|
|
|
|
|
if (rule_buf) {
|
|
|
|
useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
|
|
|
|
if (copy_to_user(useraddr, rule_buf,
|
|
|
|
info.rule_cnt * sizeof(u32)))
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
err_out:
|
2009-04-01 06:06:26 +08:00
|
|
|
kfree(rule_buf);
|
2009-02-20 16:58:13 +08:00
|
|
|
|
|
|
|
return ret;
|
2008-07-02 18:47:41 +08:00
|
|
|
}
|
|
|
|
|
2014-04-21 18:07:59 +08:00
|
|
|
static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
|
|
|
|
struct ethtool_rxnfc *rx_rings,
|
|
|
|
u32 size)
|
|
|
|
{
|
2014-05-15 07:46:45 +08:00
|
|
|
int i;
|
2014-04-21 18:07:59 +08:00
|
|
|
|
|
|
|
if (copy_from_user(indir, useraddr, size * sizeof(indir[0])))
|
2014-05-15 07:46:45 +08:00
|
|
|
return -EFAULT;
|
2014-04-21 18:07:59 +08:00
|
|
|
|
|
|
|
/* Validate ring indices */
|
2014-05-15 07:46:45 +08:00
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
if (indir[i] >= rx_rings->data)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
2014-04-21 18:07:59 +08:00
|
|
|
}
|
|
|
|
|
2016-02-02 11:51:16 +08:00
|
|
|
u8 netdev_rss_key[NETDEV_RSS_KEY_LEN] __read_mostly;
|
2014-11-16 22:23:05 +08:00
|
|
|
|
|
|
|
void netdev_rss_key_fill(void *buffer, size_t len)
|
|
|
|
{
|
|
|
|
BUG_ON(len > sizeof(netdev_rss_key));
|
|
|
|
net_get_random_once(netdev_rss_key, sizeof(netdev_rss_key));
|
|
|
|
memcpy(buffer, netdev_rss_key, len);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(netdev_rss_key_fill);
|
|
|
|
|
2016-02-09 08:05:03 +08:00
|
|
|
static int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max)
|
|
|
|
{
|
|
|
|
u32 dev_size, current_max = 0;
|
|
|
|
u32 *indir;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_rxfh_indir_size ||
|
|
|
|
!dev->ethtool_ops->get_rxfh)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
|
|
|
|
if (dev_size == 0)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
|
|
|
|
if (!indir)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while (dev_size--)
|
|
|
|
current_max = max(current_max, indir[dev_size]);
|
|
|
|
|
|
|
|
*max = current_max;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(indir);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-06-30 13:05:23 +08:00
|
|
|
static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
2011-12-15 21:55:01 +08:00
|
|
|
u32 user_size, dev_size;
|
|
|
|
u32 *indir;
|
2010-06-30 13:05:23 +08:00
|
|
|
int ret;
|
|
|
|
|
2011-12-15 21:55:01 +08:00
|
|
|
if (!dev->ethtool_ops->get_rxfh_indir_size ||
|
2014-05-15 08:25:27 +08:00
|
|
|
!dev->ethtool_ops->get_rxfh)
|
2011-12-15 21:55:01 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
|
|
|
|
if (dev_size == 0)
|
2010-06-30 13:05:23 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2011-12-15 21:55:01 +08:00
|
|
|
if (copy_from_user(&user_size,
|
2010-06-30 13:05:23 +08:00
|
|
|
useraddr + offsetof(struct ethtool_rxfh_indir, size),
|
2011-12-15 21:55:01 +08:00
|
|
|
sizeof(user_size)))
|
2010-06-30 13:05:23 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2011-12-15 21:55:01 +08:00
|
|
|
if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size),
|
|
|
|
&dev_size, sizeof(dev_size)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* If the user buffer size is 0, this is just a query for the
|
|
|
|
* device table size. Otherwise, if it's smaller than the
|
|
|
|
* device table size it's an error.
|
|
|
|
*/
|
|
|
|
if (user_size < dev_size)
|
|
|
|
return user_size == 0 ? 0 : -EINVAL;
|
|
|
|
|
|
|
|
indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
|
2010-06-30 13:05:23 +08:00
|
|
|
if (!indir)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-12-03 00:12:10 +08:00
|
|
|
ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
|
2010-06-30 13:05:23 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2011-12-15 21:55:01 +08:00
|
|
|
if (copy_to_user(useraddr +
|
|
|
|
offsetof(struct ethtool_rxfh_indir, ring_index[0]),
|
|
|
|
indir, dev_size * sizeof(indir[0])))
|
2010-06-30 13:05:23 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(indir);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
2011-12-15 21:55:01 +08:00
|
|
|
struct ethtool_rxnfc rx_rings;
|
|
|
|
u32 user_size, dev_size, i;
|
|
|
|
u32 *indir;
|
2013-01-07 17:02:08 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2010-06-30 13:05:23 +08:00
|
|
|
int ret;
|
2014-04-21 18:07:59 +08:00
|
|
|
u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]);
|
2010-06-30 13:05:23 +08:00
|
|
|
|
2014-05-15 08:25:27 +08:00
|
|
|
if (!ops->get_rxfh_indir_size || !ops->set_rxfh ||
|
2013-01-07 17:02:08 +08:00
|
|
|
!ops->get_rxnfc)
|
2011-12-15 21:55:01 +08:00
|
|
|
return -EOPNOTSUPP;
|
2013-01-07 17:02:08 +08:00
|
|
|
|
|
|
|
dev_size = ops->get_rxfh_indir_size(dev);
|
2011-12-15 21:55:01 +08:00
|
|
|
if (dev_size == 0)
|
2010-06-30 13:05:23 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2011-12-15 21:55:01 +08:00
|
|
|
if (copy_from_user(&user_size,
|
2010-06-30 13:05:23 +08:00
|
|
|
useraddr + offsetof(struct ethtool_rxfh_indir, size),
|
2011-12-15 21:55:01 +08:00
|
|
|
sizeof(user_size)))
|
2010-06-30 13:05:23 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2011-12-15 21:56:49 +08:00
|
|
|
if (user_size != 0 && user_size != dev_size)
|
2011-12-15 21:55:01 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
|
2010-06-30 13:05:23 +08:00
|
|
|
if (!indir)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2011-12-15 21:55:01 +08:00
|
|
|
rx_rings.cmd = ETHTOOL_GRXRINGS;
|
2013-01-07 17:02:08 +08:00
|
|
|
ret = ops->get_rxnfc(dev, &rx_rings, NULL);
|
2011-12-15 21:55:01 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
2011-12-15 21:56:49 +08:00
|
|
|
|
|
|
|
if (user_size == 0) {
|
|
|
|
for (i = 0; i < dev_size; i++)
|
|
|
|
indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
|
|
|
|
} else {
|
2014-04-21 18:07:59 +08:00
|
|
|
ret = ethtool_copy_validate_indir(indir,
|
|
|
|
useraddr + ringidx_offset,
|
|
|
|
&rx_rings,
|
|
|
|
dev_size);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-12-03 00:12:10 +08:00
|
|
|
ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
|
2016-02-09 08:05:03 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* indicate whether rxfh was set to default */
|
|
|
|
if (user_size == 0)
|
|
|
|
dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
|
|
|
|
else
|
|
|
|
dev->priv_flags |= IFF_RXFH_CONFIGURED;
|
2014-04-21 18:07:59 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(indir);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2014-05-15 23:28:07 +08:00
|
|
|
u32 user_indir_size, user_key_size;
|
2014-04-21 18:07:59 +08:00
|
|
|
u32 dev_indir_size = 0, dev_key_size = 0;
|
2014-05-15 23:28:07 +08:00
|
|
|
struct ethtool_rxfh rxfh;
|
2014-04-21 18:07:59 +08:00
|
|
|
u32 total_size;
|
2014-05-15 23:28:07 +08:00
|
|
|
u32 indir_bytes;
|
2014-04-21 18:07:59 +08:00
|
|
|
u32 *indir = NULL;
|
2014-12-03 00:12:10 +08:00
|
|
|
u8 dev_hfunc = 0;
|
2014-04-21 18:07:59 +08:00
|
|
|
u8 *hkey = NULL;
|
|
|
|
u8 *rss_config;
|
|
|
|
|
2014-12-03 00:12:10 +08:00
|
|
|
if (!ops->get_rxfh)
|
2014-04-21 18:07:59 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (ops->get_rxfh_indir_size)
|
|
|
|
dev_indir_size = ops->get_rxfh_indir_size(dev);
|
|
|
|
if (ops->get_rxfh_key_size)
|
|
|
|
dev_key_size = ops->get_rxfh_key_size(dev);
|
|
|
|
|
2014-05-15 23:28:07 +08:00
|
|
|
if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
|
2014-04-21 18:07:59 +08:00
|
|
|
return -EFAULT;
|
2014-05-15 23:28:07 +08:00
|
|
|
user_indir_size = rxfh.indir_size;
|
|
|
|
user_key_size = rxfh.key_size;
|
2014-04-21 18:07:59 +08:00
|
|
|
|
2014-05-15 23:28:07 +08:00
|
|
|
/* Check that reserved fields are 0 for now */
|
2018-03-08 23:45:03 +08:00
|
|
|
if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32)
|
2014-05-15 23:28:07 +08:00
|
|
|
return -EINVAL;
|
2018-03-08 23:45:03 +08:00
|
|
|
/* Most drivers don't handle rss_context, check it's 0 as well */
|
|
|
|
if (rxfh.rss_context && !ops->get_rxfh_context)
|
|
|
|
return -EOPNOTSUPP;
|
2014-05-15 23:28:07 +08:00
|
|
|
|
|
|
|
rxfh.indir_size = dev_indir_size;
|
|
|
|
rxfh.key_size = dev_key_size;
|
|
|
|
if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
|
2014-04-21 18:07:59 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
|
|
|
|
(user_key_size && (user_key_size != dev_key_size)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
indir_bytes = user_indir_size * sizeof(indir[0]);
|
|
|
|
total_size = indir_bytes + user_key_size;
|
|
|
|
rss_config = kzalloc(total_size, GFP_USER);
|
|
|
|
if (!rss_config)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (user_indir_size)
|
|
|
|
indir = (u32 *)rss_config;
|
|
|
|
|
|
|
|
if (user_key_size)
|
|
|
|
hkey = rss_config + indir_bytes;
|
|
|
|
|
2018-03-08 23:45:03 +08:00
|
|
|
if (rxfh.rss_context)
|
|
|
|
ret = dev->ethtool_ops->get_rxfh_context(dev, indir, hkey,
|
|
|
|
&dev_hfunc,
|
|
|
|
rxfh.rss_context);
|
|
|
|
else
|
|
|
|
ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc);
|
2014-12-03 00:12:10 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
2014-04-21 18:07:59 +08:00
|
|
|
|
2014-12-03 00:12:10 +08:00
|
|
|
if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, hfunc),
|
|
|
|
&dev_hfunc, sizeof(rxfh.hfunc))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
} else if (copy_to_user(useraddr +
|
|
|
|
offsetof(struct ethtool_rxfh, rss_config[0]),
|
|
|
|
rss_config, total_size)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
}
|
|
|
|
out:
|
2014-04-21 18:07:59 +08:00
|
|
|
kfree(rss_config);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
struct ethtool_rxnfc rx_rings;
|
2014-05-15 23:28:07 +08:00
|
|
|
struct ethtool_rxfh rxfh;
|
|
|
|
u32 dev_indir_size = 0, dev_key_size = 0, i;
|
2014-04-21 18:07:59 +08:00
|
|
|
u32 *indir = NULL, indir_bytes = 0;
|
|
|
|
u8 *hkey = NULL;
|
|
|
|
u8 *rss_config;
|
|
|
|
u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
|
2018-03-08 23:45:03 +08:00
|
|
|
bool delete = false;
|
2014-04-21 18:07:59 +08:00
|
|
|
|
2014-12-03 00:12:10 +08:00
|
|
|
if (!ops->get_rxnfc || !ops->set_rxfh)
|
2014-04-21 18:07:59 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (ops->get_rxfh_indir_size)
|
|
|
|
dev_indir_size = ops->get_rxfh_indir_size(dev);
|
|
|
|
if (ops->get_rxfh_key_size)
|
2015-02-20 18:54:05 +08:00
|
|
|
dev_key_size = ops->get_rxfh_key_size(dev);
|
2014-04-21 18:07:59 +08:00
|
|
|
|
2014-05-15 23:28:07 +08:00
|
|
|
if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
|
2014-04-21 18:07:59 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2014-05-15 23:28:07 +08:00
|
|
|
/* Check that reserved fields are 0 for now */
|
2018-03-08 23:45:03 +08:00
|
|
|
if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32)
|
2014-05-15 23:28:07 +08:00
|
|
|
return -EINVAL;
|
2018-03-08 23:45:03 +08:00
|
|
|
/* Most drivers don't handle rss_context, check it's 0 as well */
|
|
|
|
if (rxfh.rss_context && !ops->set_rxfh_context)
|
|
|
|
return -EOPNOTSUPP;
|
2014-05-15 23:28:07 +08:00
|
|
|
|
2014-12-03 00:12:10 +08:00
|
|
|
/* If either indir, hash key or function is valid, proceed further.
|
|
|
|
* Must request at least one change: indir size, hash key or function.
|
2014-04-21 18:07:59 +08:00
|
|
|
*/
|
2014-05-15 23:28:07 +08:00
|
|
|
if ((rxfh.indir_size &&
|
|
|
|
rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
|
|
|
|
rxfh.indir_size != dev_indir_size) ||
|
|
|
|
(rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
|
|
|
|
(rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
|
2014-12-03 00:12:10 +08:00
|
|
|
rxfh.key_size == 0 && rxfh.hfunc == ETH_RSS_HASH_NO_CHANGE))
|
2014-04-21 18:07:59 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-05-15 23:28:07 +08:00
|
|
|
if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
|
2014-04-21 18:07:59 +08:00
|
|
|
indir_bytes = dev_indir_size * sizeof(indir[0]);
|
|
|
|
|
2014-05-15 23:28:07 +08:00
|
|
|
rss_config = kzalloc(indir_bytes + rxfh.key_size, GFP_USER);
|
2014-04-21 18:07:59 +08:00
|
|
|
if (!rss_config)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rx_rings.cmd = ETHTOOL_GRXRINGS;
|
|
|
|
ret = ops->get_rxnfc(dev, &rx_rings, NULL);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2018-03-08 23:45:03 +08:00
|
|
|
/* rxfh.indir_size == 0 means reset the indir table to default (master
|
|
|
|
* context) or delete the context (other RSS contexts).
|
2014-05-15 23:28:07 +08:00
|
|
|
* rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged.
|
2014-04-21 18:07:59 +08:00
|
|
|
*/
|
2014-05-15 23:28:07 +08:00
|
|
|
if (rxfh.indir_size &&
|
|
|
|
rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) {
|
2014-04-21 18:07:59 +08:00
|
|
|
indir = (u32 *)rss_config;
|
|
|
|
ret = ethtool_copy_validate_indir(indir,
|
|
|
|
useraddr + rss_cfg_offset,
|
|
|
|
&rx_rings,
|
2014-05-15 23:28:07 +08:00
|
|
|
rxfh.indir_size);
|
2014-04-21 18:07:59 +08:00
|
|
|
if (ret)
|
2011-12-15 21:55:01 +08:00
|
|
|
goto out;
|
2014-05-15 23:28:07 +08:00
|
|
|
} else if (rxfh.indir_size == 0) {
|
2018-03-08 23:45:03 +08:00
|
|
|
if (rxfh.rss_context == 0) {
|
|
|
|
indir = (u32 *)rss_config;
|
|
|
|
for (i = 0; i < dev_indir_size; i++)
|
|
|
|
indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
|
|
|
|
} else {
|
|
|
|
delete = true;
|
|
|
|
}
|
2014-04-21 18:07:59 +08:00
|
|
|
}
|
2011-12-15 21:56:49 +08:00
|
|
|
|
2014-05-15 23:28:07 +08:00
|
|
|
if (rxfh.key_size) {
|
2014-04-21 18:07:59 +08:00
|
|
|
hkey = rss_config + indir_bytes;
|
|
|
|
if (copy_from_user(hkey,
|
|
|
|
useraddr + rss_cfg_offset + indir_bytes,
|
2014-05-15 23:28:07 +08:00
|
|
|
rxfh.key_size)) {
|
2014-04-21 18:07:59 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto out;
|
2011-12-15 21:56:49 +08:00
|
|
|
}
|
2011-12-15 21:55:01 +08:00
|
|
|
}
|
|
|
|
|
2018-03-08 23:45:03 +08:00
|
|
|
if (rxfh.rss_context)
|
|
|
|
ret = ops->set_rxfh_context(dev, indir, hkey, rxfh.hfunc,
|
|
|
|
&rxfh.rss_context, delete);
|
|
|
|
else
|
|
|
|
ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
|
2016-02-09 08:05:03 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2018-03-08 23:45:03 +08:00
|
|
|
if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context),
|
|
|
|
&rxfh.rss_context, sizeof(rxfh.rss_context)))
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
|
|
|
if (!rxfh.rss_context) {
|
|
|
|
/* indicate whether rxfh was set to default */
|
|
|
|
if (rxfh.indir_size == 0)
|
|
|
|
dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
|
|
|
|
else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
|
|
|
|
dev->priv_flags |= IFF_RXFH_CONFIGURED;
|
|
|
|
}
|
2010-06-30 13:05:23 +08:00
|
|
|
|
|
|
|
out:
|
2014-04-21 18:07:59 +08:00
|
|
|
kfree(rss_config);
|
2010-06-30 13:05:23 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_regs regs;
|
2006-09-09 02:16:13 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
void *regbuf;
|
|
|
|
int reglen, ret;
|
|
|
|
|
|
|
|
if (!ops->get_regs || !ops->get_regs_len)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(®s, useraddr, sizeof(regs)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
reglen = ops->get_regs_len(dev);
|
2018-12-26 19:51:46 +08:00
|
|
|
if (reglen <= 0)
|
|
|
|
return reglen;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (regs.len > reglen)
|
|
|
|
regs.len = reglen;
|
|
|
|
|
2019-02-01 16:24:06 +08:00
|
|
|
regbuf = vzalloc(reglen);
|
|
|
|
if (!regbuf)
|
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-06-04 04:57:13 +08:00
|
|
|
if (regs.len < reglen)
|
|
|
|
reglen = regs.len;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
ops->get_regs(dev, ®s, regbuf);
|
|
|
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(useraddr, ®s, sizeof(regs)))
|
|
|
|
goto out;
|
|
|
|
useraddr += offsetof(struct ethtool_regs, data);
|
2019-06-04 04:57:13 +08:00
|
|
|
if (copy_to_user(useraddr, regbuf, reglen))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
2010-09-20 16:42:17 +08:00
|
|
|
vfree(regbuf);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-05 18:59:58 +08:00
|
|
|
static int ethtool_reset(struct net_device *dev, char __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_value reset;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->reset)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&reset, useraddr, sizeof(reset)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
ret = dev->ethtool_ops->reset(dev, &reset.data);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &reset, sizeof(reset)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
|
|
|
|
{
|
2019-10-26 15:54:16 +08:00
|
|
|
struct ethtool_wolinfo wol;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_wol)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2019-10-26 15:54:16 +08:00
|
|
|
memset(&wol, 0, sizeof(struct ethtool_wolinfo));
|
|
|
|
wol.cmd = ETHTOOL_GWOL;
|
2005-04-17 06:20:36 +08:00
|
|
|
dev->ethtool_ops->get_wol(dev, &wol);
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &wol, sizeof(wol)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_wolinfo wol;
|
2018-09-25 03:58:59 +08:00
|
|
|
int ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!dev->ethtool_ops->set_wol)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&wol, useraddr, sizeof(wol)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2018-09-25 03:58:59 +08:00
|
|
|
ret = dev->ethtool_ops->set_wol(dev, &wol);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
dev->wol_enabled = !!wol.wolopts;
|
2020-01-27 06:11:19 +08:00
|
|
|
ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL);
|
2018-09-25 03:58:59 +08:00
|
|
|
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-06-07 01:13:06 +08:00
|
|
|
static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_eee edata;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_eee)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
memset(&edata, 0, sizeof(struct ethtool_eee));
|
|
|
|
edata.cmd = ETHTOOL_GEEE;
|
|
|
|
rc = dev->ethtool_ops->get_eee(dev, &edata);
|
|
|
|
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &edata, sizeof(edata)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_set_eee(struct net_device *dev, char __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_eee edata;
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->set_eee)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return dev->ethtool_ops->set_eee(dev, &edata);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int ethtool_nway_reset(struct net_device *dev)
|
|
|
|
{
|
|
|
|
if (!dev->ethtool_ops->nway_reset)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return dev->ethtool_ops->nway_reset(dev);
|
|
|
|
}
|
|
|
|
|
2010-12-09 20:08:35 +08:00
|
|
|
static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
|
2019-12-27 22:56:23 +08:00
|
|
|
int link = __ethtool_get_link(dev);
|
2010-12-09 20:08:35 +08:00
|
|
|
|
2019-12-27 22:56:23 +08:00
|
|
|
if (link < 0)
|
|
|
|
return link;
|
2010-12-09 20:08:35 +08:00
|
|
|
|
2019-12-27 22:56:23 +08:00
|
|
|
edata.data = link;
|
2010-12-09 20:08:35 +08:00
|
|
|
if (copy_to_user(useraddr, &edata, sizeof(edata)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-12 08:42:12 +08:00
|
|
|
static int ethtool_get_any_eeprom(struct net_device *dev, void __user *useraddr,
|
|
|
|
int (*getter)(struct net_device *,
|
|
|
|
struct ethtool_eeprom *, u8 *),
|
|
|
|
u32 total_len)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ethtool_eeprom eeprom;
|
2008-04-16 10:24:17 +08:00
|
|
|
void __user *userbuf = useraddr + sizeof(eeprom);
|
|
|
|
u32 bytes_remaining;
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 *data;
|
2008-04-16 10:24:17 +08:00
|
|
|
int ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* Check for wrap and zero */
|
|
|
|
if (eeprom.offset + eeprom.len <= eeprom.offset)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Check for exceeding total eeprom len */
|
2012-04-12 08:42:12 +08:00
|
|
|
if (eeprom.offset + eeprom.len > total_len)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-04-16 10:24:17 +08:00
|
|
|
data = kmalloc(PAGE_SIZE, GFP_USER);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-04-16 10:24:17 +08:00
|
|
|
bytes_remaining = eeprom.len;
|
|
|
|
while (bytes_remaining > 0) {
|
|
|
|
eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
|
|
|
|
|
2012-04-12 08:42:12 +08:00
|
|
|
ret = getter(dev, &eeprom, data);
|
2008-04-16 10:24:17 +08:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
if (copy_to_user(userbuf, data, eeprom.len)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
userbuf += eeprom.len;
|
|
|
|
eeprom.offset += eeprom.len;
|
|
|
|
bytes_remaining -= eeprom.len;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-25 11:55:56 +08:00
|
|
|
eeprom.len = userbuf - (useraddr + sizeof(eeprom));
|
|
|
|
eeprom.offset -= eeprom.len;
|
|
|
|
if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
|
|
|
|
ret = -EFAULT;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-12 08:42:12 +08:00
|
|
|
static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
|
2014-10-31 11:50:15 +08:00
|
|
|
if (!ops->get_eeprom || !ops->get_eeprom_len ||
|
|
|
|
!ops->get_eeprom_len(dev))
|
2012-04-12 08:42:12 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom,
|
|
|
|
ops->get_eeprom_len(dev));
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_eeprom eeprom;
|
2006-09-09 02:16:13 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2008-04-16 10:24:17 +08:00
|
|
|
void __user *userbuf = useraddr + sizeof(eeprom);
|
|
|
|
u32 bytes_remaining;
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 *data;
|
2008-04-16 10:24:17 +08:00
|
|
|
int ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-10-31 11:50:15 +08:00
|
|
|
if (!ops->set_eeprom || !ops->get_eeprom_len ||
|
|
|
|
!ops->get_eeprom_len(dev))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* Check for wrap and zero */
|
|
|
|
if (eeprom.offset + eeprom.len <= eeprom.offset)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Check for exceeding total eeprom len */
|
|
|
|
if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-04-16 10:24:17 +08:00
|
|
|
data = kmalloc(PAGE_SIZE, GFP_USER);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-04-16 10:24:17 +08:00
|
|
|
bytes_remaining = eeprom.len;
|
|
|
|
while (bytes_remaining > 0) {
|
|
|
|
eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
|
|
|
|
|
|
|
|
if (copy_from_user(data, userbuf, eeprom.len)) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = ops->set_eeprom(dev, &eeprom, data);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
userbuf += eeprom.len;
|
|
|
|
eeprom.offset += eeprom.len;
|
|
|
|
bytes_remaining -= eeprom.len;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
kfree(data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-08 12:54:42 +08:00
|
|
|
static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-02-12 04:14:23 +08:00
|
|
|
struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_coalesce)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
dev->ethtool_ops->get_coalesce(dev, &coalesce);
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-05 13:15:31 +08:00
|
|
|
static bool
|
|
|
|
ethtool_set_coalesce_supported(struct net_device *dev,
|
|
|
|
struct ethtool_coalesce *coalesce)
|
|
|
|
{
|
|
|
|
u32 supported_params = dev->ethtool_ops->supported_coalesce_params;
|
|
|
|
u32 nonzero_params = 0;
|
|
|
|
|
|
|
|
if (!supported_params)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (coalesce->rx_coalesce_usecs)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RX_USECS;
|
|
|
|
if (coalesce->rx_max_coalesced_frames)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES;
|
|
|
|
if (coalesce->rx_coalesce_usecs_irq)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RX_USECS_IRQ;
|
|
|
|
if (coalesce->rx_max_coalesced_frames_irq)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ;
|
|
|
|
if (coalesce->tx_coalesce_usecs)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_TX_USECS;
|
|
|
|
if (coalesce->tx_max_coalesced_frames)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES;
|
|
|
|
if (coalesce->tx_coalesce_usecs_irq)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_TX_USECS_IRQ;
|
|
|
|
if (coalesce->tx_max_coalesced_frames_irq)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ;
|
|
|
|
if (coalesce->stats_block_coalesce_usecs)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_STATS_BLOCK_USECS;
|
|
|
|
if (coalesce->use_adaptive_rx_coalesce)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_RX;
|
|
|
|
if (coalesce->use_adaptive_tx_coalesce)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_TX;
|
|
|
|
if (coalesce->pkt_rate_low)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_LOW;
|
|
|
|
if (coalesce->rx_coalesce_usecs_low)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RX_USECS_LOW;
|
|
|
|
if (coalesce->rx_max_coalesced_frames_low)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW;
|
|
|
|
if (coalesce->tx_coalesce_usecs_low)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_TX_USECS_LOW;
|
|
|
|
if (coalesce->tx_max_coalesced_frames_low)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW;
|
|
|
|
if (coalesce->pkt_rate_high)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_HIGH;
|
|
|
|
if (coalesce->rx_coalesce_usecs_high)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RX_USECS_HIGH;
|
|
|
|
if (coalesce->rx_max_coalesced_frames_high)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH;
|
|
|
|
if (coalesce->tx_coalesce_usecs_high)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_TX_USECS_HIGH;
|
|
|
|
if (coalesce->tx_max_coalesced_frames_high)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH;
|
|
|
|
if (coalesce->rate_sample_interval)
|
|
|
|
nonzero_params |= ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL;
|
|
|
|
|
|
|
|
return (supported_params & nonzero_params) == nonzero_params;
|
|
|
|
}
|
|
|
|
|
2010-04-08 12:54:42 +08:00
|
|
|
static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ethtool_coalesce coalesce;
|
|
|
|
|
2005-06-07 06:07:19 +08:00
|
|
|
if (!dev->ethtool_ops->set_coalesce)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2020-03-05 13:15:31 +08:00
|
|
|
if (!ethtool_set_coalesce_supported(dev, &coalesce))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return dev->ethtool_ops->set_coalesce(dev, &coalesce);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
2010-02-12 04:14:23 +08:00
|
|
|
struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM };
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_ringparam)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
dev->ethtool_ops->get_ringparam(dev, &ringparam);
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &ringparam, sizeof(ringparam)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
2018-01-08 22:00:24 +08:00
|
|
|
struct ethtool_ringparam ringparam, max = { .cmd = ETHTOOL_GRINGPARAM };
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-01-08 22:00:24 +08:00
|
|
|
if (!dev->ethtool_ops->set_ringparam || !dev->ethtool_ops->get_ringparam)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&ringparam, useraddr, sizeof(ringparam)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2018-01-08 22:00:24 +08:00
|
|
|
dev->ethtool_ops->get_ringparam(dev, &max);
|
|
|
|
|
|
|
|
/* ensure new ring parameters are within the maximums */
|
|
|
|
if (ringparam.rx_pending > max.rx_max_pending ||
|
|
|
|
ringparam.rx_mini_pending > max.rx_mini_max_pending ||
|
|
|
|
ringparam.rx_jumbo_pending > max.rx_jumbo_max_pending ||
|
|
|
|
ringparam.tx_pending > max.tx_max_pending)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return dev->ethtool_ops->set_ringparam(dev, &ringparam);
|
|
|
|
}
|
|
|
|
|
2011-04-07 09:58:42 +08:00
|
|
|
static noinline_for_stack int ethtool_get_channels(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS };
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_channels)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
dev->ethtool_ops->get_channels(dev, &channels);
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &channels, sizeof(channels)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
2018-10-01 20:51:35 +08:00
|
|
|
struct ethtool_channels channels, curr = { .cmd = ETHTOOL_GCHANNELS };
|
2018-10-01 20:51:36 +08:00
|
|
|
u16 from_channel, to_channel;
|
2016-02-09 08:05:03 +08:00
|
|
|
u32 max_rx_in_use = 0;
|
2018-10-01 20:51:36 +08:00
|
|
|
unsigned int i;
|
2011-04-07 09:58:42 +08:00
|
|
|
|
2016-02-09 08:05:04 +08:00
|
|
|
if (!dev->ethtool_ops->set_channels || !dev->ethtool_ops->get_channels)
|
2011-04-07 09:58:42 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&channels, useraddr, sizeof(channels)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2018-10-01 20:51:35 +08:00
|
|
|
dev->ethtool_ops->get_channels(dev, &curr);
|
2016-02-09 08:05:04 +08:00
|
|
|
|
|
|
|
/* ensure new counts are within the maximums */
|
2018-10-01 20:51:35 +08:00
|
|
|
if (channels.rx_count > curr.max_rx ||
|
|
|
|
channels.tx_count > curr.max_tx ||
|
|
|
|
channels.combined_count > curr.max_combined ||
|
|
|
|
channels.other_count > curr.max_other)
|
2016-02-09 08:05:04 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-02-09 08:05:03 +08:00
|
|
|
/* ensure the new Rx count fits within the configured Rx flow
|
|
|
|
* indirection table settings */
|
|
|
|
if (netif_is_rxfh_configured(dev) &&
|
|
|
|
!ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) &&
|
|
|
|
(channels.combined_count + channels.rx_count) <= max_rx_in_use)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-10-01 20:51:36 +08:00
|
|
|
/* Disabling channels, query zero-copy AF_XDP sockets */
|
|
|
|
from_channel = channels.combined_count +
|
|
|
|
min(channels.rx_count, channels.tx_count);
|
|
|
|
to_channel = curr.combined_count + max(curr.rx_count, curr.tx_count);
|
|
|
|
for (i = from_channel; i < to_channel; i++)
|
|
|
|
if (xdp_get_umem_from_qid(dev, i))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-04-07 09:58:42 +08:00
|
|
|
return dev->ethtool_ops->set_channels(dev, &channels);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
2019-02-27 20:47:57 +08:00
|
|
|
struct ethtool_pauseparam pauseparam = { .cmd = ETHTOOL_GPAUSEPARAM };
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_pauseparam)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
dev->ethtool_ops->get_pauseparam(dev, &pauseparam);
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_pauseparam pauseparam;
|
|
|
|
|
2006-07-18 00:54:40 +08:00
|
|
|
if (!dev->ethtool_ops->set_pauseparam)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_test test;
|
2006-09-09 02:16:13 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
u64 *data;
|
2007-08-16 07:01:08 +08:00
|
|
|
int ret, test_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-10-01 19:33:03 +08:00
|
|
|
if (!ops->self_test || !ops->get_sset_count)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2009-10-01 19:33:03 +08:00
|
|
|
test_len = ops->get_sset_count(dev, ETH_SS_TEST);
|
2007-08-16 07:01:08 +08:00
|
|
|
if (test_len < 0)
|
|
|
|
return test_len;
|
|
|
|
WARN_ON(test_len == 0);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copy_from_user(&test, useraddr, sizeof(test)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2007-08-16 07:01:08 +08:00
|
|
|
test.len = test_len;
|
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 04:55:00 +08:00
|
|
|
data = kmalloc_array(test_len, sizeof(u64), GFP_USER);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ops->self_test(dev, &test, data);
|
|
|
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(useraddr, &test, sizeof(test)))
|
|
|
|
goto out;
|
|
|
|
useraddr += sizeof(test);
|
|
|
|
if (copy_to_user(useraddr, data, test.len * sizeof(u64)))
|
|
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_get_strings(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_gstrings gstrings;
|
|
|
|
u8 *data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2011-02-16 00:59:16 +08:00
|
|
|
ret = __ethtool_get_sset_count(dev, gstrings.string_set);
|
2009-10-01 19:33:03 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2017-01-31 10:25:18 +08:00
|
|
|
if (ret > S32_MAX / ETH_GSTRING_LEN)
|
|
|
|
return -ENOMEM;
|
|
|
|
WARN_ON_ONCE(!ret);
|
2009-10-01 19:33:03 +08:00
|
|
|
|
|
|
|
gstrings.len = ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-03-29 09:18:02 +08:00
|
|
|
if (gstrings.len) {
|
|
|
|
data = vzalloc(array_size(gstrings.len, ETH_GSTRING_LEN));
|
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
__ethtool_get_strings(dev, gstrings.string_set, data);
|
|
|
|
} else {
|
|
|
|
data = NULL;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
|
|
|
|
goto out;
|
|
|
|
useraddr += sizeof(gstrings);
|
2017-01-31 10:25:18 +08:00
|
|
|
if (gstrings.len &&
|
|
|
|
copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
|
2011-02-16 00:59:16 +08:00
|
|
|
out:
|
2017-01-31 10:25:18 +08:00
|
|
|
vfree(data);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_value id;
|
2011-04-02 07:35:15 +08:00
|
|
|
static bool busy;
|
2013-01-07 17:02:08 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2011-04-02 07:35:15 +08:00
|
|
|
int rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-07 17:02:08 +08:00
|
|
|
if (!ops->set_phys_id)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2011-04-02 07:35:15 +08:00
|
|
|
if (busy)
|
|
|
|
return -EBUSY;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copy_from_user(&id, useraddr, sizeof(id)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2013-01-07 17:02:08 +08:00
|
|
|
rc = ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE);
|
2011-04-13 21:09:10 +08:00
|
|
|
if (rc < 0)
|
2011-04-02 07:35:15 +08:00
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* Drop the RTNL lock while waiting, but prevent reentry or
|
|
|
|
* removal of the device.
|
|
|
|
*/
|
|
|
|
busy = true;
|
|
|
|
dev_hold(dev);
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
if (rc == 0) {
|
|
|
|
/* Driver will handle this itself */
|
|
|
|
schedule_timeout_interruptible(
|
2011-04-11 21:01:59 +08:00
|
|
|
id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT);
|
2011-04-02 07:35:15 +08:00
|
|
|
} else {
|
2011-04-13 21:09:10 +08:00
|
|
|
/* Driver expects to be called at twice the frequency in rc */
|
|
|
|
int n = rc * 2, i, interval = HZ / n;
|
|
|
|
|
|
|
|
/* Count down seconds */
|
2011-04-02 07:35:15 +08:00
|
|
|
do {
|
2011-04-13 21:09:10 +08:00
|
|
|
/* Count down iterations per second */
|
|
|
|
i = n;
|
|
|
|
do {
|
|
|
|
rtnl_lock();
|
2013-01-07 17:02:08 +08:00
|
|
|
rc = ops->set_phys_id(dev,
|
2011-04-13 21:09:10 +08:00
|
|
|
(i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON);
|
|
|
|
rtnl_unlock();
|
|
|
|
if (rc)
|
|
|
|
break;
|
|
|
|
schedule_timeout_interruptible(interval);
|
|
|
|
} while (!signal_pending(current) && --i != 0);
|
2011-04-02 07:35:15 +08:00
|
|
|
} while (!signal_pending(current) &&
|
|
|
|
(id.data == 0 || --id.data != 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
dev_put(dev);
|
|
|
|
busy = false;
|
|
|
|
|
2013-01-07 17:02:08 +08:00
|
|
|
(void) ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE);
|
2011-04-02 07:35:15 +08:00
|
|
|
return rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_stats stats;
|
2006-09-09 02:16:13 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
u64 *data;
|
2007-08-16 07:01:08 +08:00
|
|
|
int ret, n_stats;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-10-01 19:33:03 +08:00
|
|
|
if (!ops->get_ethtool_stats || !ops->get_sset_count)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2009-10-01 19:33:03 +08:00
|
|
|
n_stats = ops->get_sset_count(dev, ETH_SS_STATS);
|
2007-08-16 07:01:08 +08:00
|
|
|
if (n_stats < 0)
|
|
|
|
return n_stats;
|
2017-01-31 10:25:18 +08:00
|
|
|
if (n_stats > S32_MAX / sizeof(u64))
|
|
|
|
return -ENOMEM;
|
|
|
|
WARN_ON_ONCE(!n_stats);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copy_from_user(&stats, useraddr, sizeof(stats)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2007-08-16 07:01:08 +08:00
|
|
|
stats.n_stats = n_stats;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-03-29 09:18:02 +08:00
|
|
|
if (n_stats) {
|
|
|
|
data = vzalloc(array_size(n_stats, sizeof(u64)));
|
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
ops->get_ethtool_stats(dev, &stats, data);
|
|
|
|
} else {
|
|
|
|
data = NULL;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(useraddr, &stats, sizeof(stats)))
|
|
|
|
goto out;
|
|
|
|
useraddr += sizeof(stats);
|
2017-01-31 10:25:18 +08:00
|
|
|
if (n_stats && copy_to_user(useraddr, data, n_stats * sizeof(u64)))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
2017-01-31 10:25:18 +08:00
|
|
|
vfree(data);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-12-30 23:28:25 +08:00
|
|
|
static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
2018-04-26 03:12:48 +08:00
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
2015-12-30 23:28:25 +08:00
|
|
|
struct phy_device *phydev = dev->phydev;
|
2018-04-26 03:12:48 +08:00
|
|
|
struct ethtool_stats stats;
|
2015-12-30 23:28:25 +08:00
|
|
|
u64 *data;
|
|
|
|
int ret, n_stats;
|
|
|
|
|
2018-04-26 03:12:48 +08:00
|
|
|
if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
|
2015-12-30 23:28:25 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2018-04-26 03:12:48 +08:00
|
|
|
if (dev->phydev && !ops->get_ethtool_phy_stats)
|
|
|
|
n_stats = phy_ethtool_get_sset_count(dev->phydev);
|
|
|
|
else
|
|
|
|
n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
|
2015-12-30 23:28:25 +08:00
|
|
|
if (n_stats < 0)
|
|
|
|
return n_stats;
|
2017-01-31 10:25:18 +08:00
|
|
|
if (n_stats > S32_MAX / sizeof(u64))
|
|
|
|
return -ENOMEM;
|
|
|
|
WARN_ON_ONCE(!n_stats);
|
2015-12-30 23:28:25 +08:00
|
|
|
|
|
|
|
if (copy_from_user(&stats, useraddr, sizeof(stats)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
stats.n_stats = n_stats;
|
|
|
|
|
2019-03-29 09:18:02 +08:00
|
|
|
if (n_stats) {
|
|
|
|
data = vzalloc(array_size(n_stats, sizeof(u64)));
|
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (dev->phydev && !ops->get_ethtool_phy_stats) {
|
|
|
|
ret = phy_ethtool_get_stats(dev->phydev, &stats, data);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
ops->get_ethtool_phy_stats(dev, &stats, data);
|
|
|
|
}
|
2018-04-26 03:12:48 +08:00
|
|
|
} else {
|
2019-03-29 09:18:02 +08:00
|
|
|
data = NULL;
|
2018-04-26 03:12:48 +08:00
|
|
|
}
|
2015-12-30 23:28:25 +08:00
|
|
|
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(useraddr, &stats, sizeof(stats)))
|
|
|
|
goto out;
|
|
|
|
useraddr += sizeof(stats);
|
2017-01-31 10:25:18 +08:00
|
|
|
if (n_stats && copy_to_user(useraddr, data, n_stats * sizeof(u64)))
|
2015-12-30 23:28:25 +08:00
|
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
2017-01-31 10:25:18 +08:00
|
|
|
vfree(data);
|
2015-12-30 23:28:25 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-09-05 10:26:18 +08:00
|
|
|
static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
|
2005-08-21 08:15:54 +08:00
|
|
|
{
|
|
|
|
struct ethtool_perm_addr epaddr;
|
|
|
|
|
2007-08-01 05:00:29 +08:00
|
|
|
if (copy_from_user(&epaddr, useraddr, sizeof(epaddr)))
|
2005-08-21 08:15:54 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2007-08-01 05:00:29 +08:00
|
|
|
if (epaddr.size < dev->addr_len)
|
|
|
|
return -ETOOSMALL;
|
|
|
|
epaddr.size = dev->addr_len;
|
2005-08-21 08:15:54 +08:00
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &epaddr, sizeof(epaddr)))
|
2007-08-01 05:00:29 +08:00
|
|
|
return -EFAULT;
|
2005-08-21 08:15:54 +08:00
|
|
|
useraddr += sizeof(epaddr);
|
2007-08-01 05:00:29 +08:00
|
|
|
if (copy_to_user(useraddr, dev->perm_addr, epaddr.size))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
2005-08-21 08:15:54 +08:00
|
|
|
}
|
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
static int ethtool_get_value(struct net_device *dev, char __user *useraddr,
|
|
|
|
u32 cmd, u32 (*actor)(struct net_device *))
|
2007-08-16 07:00:51 +08:00
|
|
|
{
|
2010-02-12 04:14:23 +08:00
|
|
|
struct ethtool_value edata = { .cmd = cmd };
|
2007-08-16 07:00:51 +08:00
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
if (!actor)
|
2007-08-16 07:00:51 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
edata.data = actor(dev);
|
2007-08-16 07:00:51 +08:00
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &edata, sizeof(edata)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
static int ethtool_set_value_void(struct net_device *dev, char __user *useraddr,
|
|
|
|
void (*actor)(struct net_device *, u32))
|
2007-08-16 07:00:51 +08:00
|
|
|
{
|
|
|
|
struct ethtool_value edata;
|
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
if (!actor)
|
2007-08-16 07:00:51 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
actor(dev, edata.data);
|
2007-08-16 07:01:32 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
|
|
|
|
int (*actor)(struct net_device *, u32))
|
2007-08-16 07:01:32 +08:00
|
|
|
{
|
|
|
|
struct ethtool_value edata;
|
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
if (!actor)
|
2007-08-16 07:01:32 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2007-08-16 07:01:56 +08:00
|
|
|
return actor(dev, edata.data);
|
2007-08-16 07:01:32 +08:00
|
|
|
}
|
|
|
|
|
2010-04-08 12:54:42 +08:00
|
|
|
static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
|
|
|
|
char __user *useraddr)
|
2009-09-03 01:02:55 +08:00
|
|
|
{
|
|
|
|
struct ethtool_flash efl;
|
|
|
|
|
|
|
|
if (copy_from_user(&efl, useraddr, sizeof(efl)))
|
|
|
|
return -EFAULT;
|
2019-02-15 05:40:45 +08:00
|
|
|
efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
|
2009-09-03 01:02:55 +08:00
|
|
|
|
2019-02-26 11:34:06 +08:00
|
|
|
if (!dev->ethtool_ops->flash_device)
|
|
|
|
return devlink_compat_flash_update(dev, efl.data);
|
2012-02-01 17:32:25 +08:00
|
|
|
|
2009-09-03 01:02:55 +08:00
|
|
|
return dev->ethtool_ops->flash_device(dev, &efl);
|
|
|
|
}
|
|
|
|
|
2011-05-12 20:48:32 +08:00
|
|
|
static int ethtool_set_dump(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_dump dump;
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->set_dump)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&dump, useraddr, sizeof(dump)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return dev->ethtool_ops->set_dump(dev, &dump);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_get_dump_flag(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ethtool_dump dump;
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
|
2013-01-07 17:02:08 +08:00
|
|
|
if (!ops->get_dump_flag)
|
2011-05-12 20:48:32 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&dump, useraddr, sizeof(dump)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
ret = ops->get_dump_flag(dev, &dump);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &dump, sizeof(dump)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_get_dump_data(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
__u32 len;
|
|
|
|
struct ethtool_dump dump, tmp;
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
void *data = NULL;
|
|
|
|
|
2013-01-07 17:02:08 +08:00
|
|
|
if (!ops->get_dump_data || !ops->get_dump_flag)
|
2011-05-12 20:48:32 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&dump, useraddr, sizeof(dump)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
|
|
tmp.cmd = ETHTOOL_GET_DUMP_FLAG;
|
|
|
|
ret = ops->get_dump_flag(dev, &tmp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2013-07-01 23:23:30 +08:00
|
|
|
len = min(tmp.len, dump.len);
|
2011-05-12 20:48:32 +08:00
|
|
|
if (!len)
|
|
|
|
return -EFAULT;
|
|
|
|
|
2013-07-01 23:23:30 +08:00
|
|
|
/* Don't ever let the driver think there's more space available
|
|
|
|
* than it requested with .get_dump_flag().
|
|
|
|
*/
|
|
|
|
dump.len = len;
|
|
|
|
|
|
|
|
/* Always allocate enough space to hold the whole thing so that the
|
|
|
|
* driver does not need to check the length and bother with partial
|
|
|
|
* dumping.
|
|
|
|
*/
|
2011-05-12 20:48:32 +08:00
|
|
|
data = vzalloc(tmp.len);
|
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = ops->get_dump_data(dev, &dump, data);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2013-07-01 23:23:30 +08:00
|
|
|
/* There are two sane possibilities:
|
|
|
|
* 1. The driver's .get_dump_data() does not touch dump.len.
|
|
|
|
* 2. Or it may set dump.len to how much it really writes, which
|
|
|
|
* should be tmp.len (or len if it can do a partial dump).
|
|
|
|
* In any case respond to userspace with the actual length of data
|
|
|
|
* it's receiving.
|
|
|
|
*/
|
|
|
|
WARN_ON(dump.len != len && dump.len != tmp.len);
|
|
|
|
dump.len = len;
|
|
|
|
|
2011-05-12 20:48:32 +08:00
|
|
|
if (copy_to_user(useraddr, &dump, sizeof(dump))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
useraddr += offsetof(struct ethtool_dump, data);
|
|
|
|
if (copy_to_user(useraddr, data, len))
|
|
|
|
ret = -EFAULT;
|
|
|
|
out:
|
|
|
|
vfree(data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-04 06:59:17 +08:00
|
|
|
static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct ethtool_ts_info info;
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
struct phy_device *phydev = dev->phydev;
|
|
|
|
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.cmd = ETHTOOL_GET_TS_INFO;
|
|
|
|
|
2019-12-26 10:16:12 +08:00
|
|
|
if (phy_has_tsinfo(phydev)) {
|
|
|
|
err = phy_ts_info(phydev, &info);
|
2013-01-07 17:02:08 +08:00
|
|
|
} else if (ops->get_ts_info) {
|
2012-04-04 06:59:17 +08:00
|
|
|
err = ops->get_ts_info(dev, &info);
|
|
|
|
} else {
|
|
|
|
info.so_timestamping =
|
|
|
|
SOF_TIMESTAMPING_RX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_SOFTWARE;
|
|
|
|
info.phc_index = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &info, sizeof(info)))
|
|
|
|
err = -EFAULT;
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-01-03 09:27:56 +08:00
|
|
|
static int __ethtool_get_module_info(struct net_device *dev,
|
|
|
|
struct ethtool_modinfo *modinfo)
|
|
|
|
{
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
struct phy_device *phydev = dev->phydev;
|
|
|
|
|
2018-03-29 06:44:16 +08:00
|
|
|
if (dev->sfp_bus)
|
|
|
|
return sfp_get_module_info(dev->sfp_bus, modinfo);
|
|
|
|
|
2015-01-03 09:27:56 +08:00
|
|
|
if (phydev && phydev->drv && phydev->drv->module_info)
|
|
|
|
return phydev->drv->module_info(phydev, modinfo);
|
|
|
|
|
|
|
|
if (ops->get_module_info)
|
|
|
|
return ops->get_module_info(dev, modinfo);
|
|
|
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2012-04-19 16:44:42 +08:00
|
|
|
static int ethtool_get_module_info(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ethtool_modinfo modinfo;
|
|
|
|
|
|
|
|
if (copy_from_user(&modinfo, useraddr, sizeof(modinfo)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2015-01-03 09:27:56 +08:00
|
|
|
ret = __ethtool_get_module_info(dev, &modinfo);
|
2012-04-19 16:44:42 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &modinfo, sizeof(modinfo)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-03 09:27:56 +08:00
|
|
|
static int __ethtool_get_module_eeprom(struct net_device *dev,
|
|
|
|
struct ethtool_eeprom *ee, u8 *data)
|
|
|
|
{
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
struct phy_device *phydev = dev->phydev;
|
|
|
|
|
2018-03-29 06:44:16 +08:00
|
|
|
if (dev->sfp_bus)
|
|
|
|
return sfp_get_module_eeprom(dev->sfp_bus, ee, data);
|
|
|
|
|
2015-01-03 09:27:56 +08:00
|
|
|
if (phydev && phydev->drv && phydev->drv->module_eeprom)
|
|
|
|
return phydev->drv->module_eeprom(phydev, ee, data);
|
|
|
|
|
|
|
|
if (ops->get_module_eeprom)
|
|
|
|
return ops->get_module_eeprom(dev, ee, data);
|
|
|
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2012-04-19 16:44:42 +08:00
|
|
|
static int ethtool_get_module_eeprom(struct net_device *dev,
|
|
|
|
void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ethtool_modinfo modinfo;
|
|
|
|
|
2015-01-03 09:27:56 +08:00
|
|
|
ret = __ethtool_get_module_info(dev, &modinfo);
|
2012-04-19 16:44:42 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2015-01-03 09:27:56 +08:00
|
|
|
return ethtool_get_any_eeprom(dev, useraddr,
|
|
|
|
__ethtool_get_module_eeprom,
|
2012-04-19 16:44:42 +08:00
|
|
|
modinfo.eeprom_len);
|
|
|
|
}
|
|
|
|
|
2014-09-03 05:47:20 +08:00
|
|
|
static int ethtool_tunable_valid(const struct ethtool_tunable *tuna)
|
|
|
|
{
|
|
|
|
switch (tuna->id) {
|
|
|
|
case ETHTOOL_RX_COPYBREAK:
|
2014-10-05 17:35:21 +08:00
|
|
|
case ETHTOOL_TX_COPYBREAK:
|
2014-09-03 05:47:20 +08:00
|
|
|
if (tuna->len != sizeof(u32) ||
|
|
|
|
tuna->type_id != ETHTOOL_TUNABLE_U32)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
2017-11-20 22:14:30 +08:00
|
|
|
case ETHTOOL_PFC_PREVENTION_TOUT:
|
|
|
|
if (tuna->len != sizeof(u16) ||
|
|
|
|
tuna->type_id != ETHTOOL_TUNABLE_U16)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
2014-09-03 05:47:20 +08:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_get_tunable(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ethtool_tunable tuna;
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
void *data;
|
|
|
|
|
|
|
|
if (!ops->get_tunable)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
|
|
|
|
return -EFAULT;
|
|
|
|
ret = ethtool_tunable_valid(&tuna);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
data = kmalloc(tuna.len, GFP_USER);
|
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
ret = ops->get_tunable(dev, &tuna, data);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
useraddr += sizeof(tuna);
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(useraddr, data, tuna.len))
|
|
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_set_tunable(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ethtool_tunable tuna;
|
|
|
|
const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
|
void *data;
|
|
|
|
|
|
|
|
if (!ops->set_tunable)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
|
|
|
|
return -EFAULT;
|
|
|
|
ret = ethtool_tunable_valid(&tuna);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
useraddr += sizeof(tuna);
|
2017-05-14 06:31:26 +08:00
|
|
|
data = memdup_user(useraddr, tuna.len);
|
|
|
|
if (IS_ERR(data))
|
|
|
|
return PTR_ERR(data);
|
2014-09-03 05:47:20 +08:00
|
|
|
ret = ops->set_tunable(dev, &tuna, data);
|
|
|
|
|
|
|
|
kfree(data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-07 23:58:35 +08:00
|
|
|
static noinline_for_stack int
|
|
|
|
ethtool_get_per_queue_coalesce(struct net_device *dev,
|
|
|
|
void __user *useraddr,
|
|
|
|
struct ethtool_per_queue_op *per_queue_opt)
|
2016-02-19 22:24:02 +08:00
|
|
|
{
|
|
|
|
u32 bit;
|
|
|
|
int ret;
|
|
|
|
DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_per_queue_coalesce)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
useraddr += sizeof(*per_queue_opt);
|
|
|
|
|
2018-02-07 07:38:06 +08:00
|
|
|
bitmap_from_arr32(queue_mask, per_queue_opt->queue_mask,
|
|
|
|
MAX_NUM_QUEUE);
|
2016-02-19 22:24:02 +08:00
|
|
|
|
|
|
|
for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
|
|
|
|
struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
|
|
|
|
|
|
|
|
ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, &coalesce);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
|
|
|
|
return -EFAULT;
|
|
|
|
useraddr += sizeof(coalesce);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-07 23:58:35 +08:00
|
|
|
static noinline_for_stack int
|
|
|
|
ethtool_set_per_queue_coalesce(struct net_device *dev,
|
|
|
|
void __user *useraddr,
|
|
|
|
struct ethtool_per_queue_op *per_queue_opt)
|
2016-02-19 22:24:03 +08:00
|
|
|
{
|
|
|
|
u32 bit;
|
|
|
|
int i, ret = 0;
|
|
|
|
int n_queue;
|
|
|
|
struct ethtool_coalesce *backup = NULL, *tmp = NULL;
|
|
|
|
DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
|
|
|
|
|
|
|
|
if ((!dev->ethtool_ops->set_per_queue_coalesce) ||
|
|
|
|
(!dev->ethtool_ops->get_per_queue_coalesce))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
useraddr += sizeof(*per_queue_opt);
|
|
|
|
|
2018-02-07 07:38:06 +08:00
|
|
|
bitmap_from_arr32(queue_mask, per_queue_opt->queue_mask, MAX_NUM_QUEUE);
|
2016-02-19 22:24:03 +08:00
|
|
|
n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE);
|
|
|
|
tmp = backup = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL);
|
|
|
|
if (!backup)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
|
|
|
|
struct ethtool_coalesce coalesce;
|
|
|
|
|
|
|
|
ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, tmp);
|
|
|
|
if (ret != 0)
|
|
|
|
goto roll_back;
|
|
|
|
|
|
|
|
tmp++;
|
|
|
|
|
|
|
|
if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
goto roll_back;
|
|
|
|
}
|
|
|
|
|
2020-03-05 13:15:31 +08:00
|
|
|
if (!ethtool_set_coalesce_supported(dev, &coalesce)) {
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto roll_back;
|
|
|
|
}
|
|
|
|
|
2016-02-19 22:24:03 +08:00
|
|
|
ret = dev->ethtool_ops->set_per_queue_coalesce(dev, bit, &coalesce);
|
|
|
|
if (ret != 0)
|
|
|
|
goto roll_back;
|
|
|
|
|
|
|
|
useraddr += sizeof(coalesce);
|
|
|
|
}
|
|
|
|
|
|
|
|
roll_back:
|
|
|
|
if (ret != 0) {
|
|
|
|
tmp = backup;
|
|
|
|
for_each_set_bit(i, queue_mask, bit) {
|
|
|
|
dev->ethtool_ops->set_per_queue_coalesce(dev, i, tmp);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kfree(backup);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-07 23:58:35 +08:00
|
|
|
static int noinline_for_stack ethtool_set_per_queue(struct net_device *dev,
|
ethtool: fix a privilege escalation bug
In dev_ethtool(), the eth command 'ethcmd' is firstly copied from the
use-space buffer 'useraddr' and checked to see whether it is
ETHTOOL_PERQUEUE. If yes, the sub-command 'sub_cmd' is further copied from
the user space. Otherwise, 'sub_cmd' is the same as 'ethcmd'. Next,
according to 'sub_cmd', a permission check is enforced through the function
ns_capable(). For example, the permission check is required if 'sub_cmd' is
ETHTOOL_SCOALESCE, but it is not necessary if 'sub_cmd' is
ETHTOOL_GCOALESCE, as suggested in the comment "Allow some commands to be
done by anyone". The following execution invokes different handlers
according to 'ethcmd'. Specifically, if 'ethcmd' is ETHTOOL_PERQUEUE,
ethtool_set_per_queue() is called. In ethtool_set_per_queue(), the kernel
object 'per_queue_opt' is copied again from the user-space buffer
'useraddr' and 'per_queue_opt.sub_command' is used to determine which
operation should be performed. Given that the buffer 'useraddr' is in the
user space, a malicious user can race to change the sub-command between the
two copies. In particular, the attacker can supply ETHTOOL_PERQUEUE and
ETHTOOL_GCOALESCE to bypass the permission check in dev_ethtool(). Then
before ethtool_set_per_queue() is called, the attacker changes
ETHTOOL_GCOALESCE to ETHTOOL_SCOALESCE. In this way, the attacker can
bypass the permission check and execute ETHTOOL_SCOALESCE.
This patch enforces a check in ethtool_set_per_queue() after the second
copy from 'useraddr'. If the sub-command is different from the one obtained
in the first copy in dev_ethtool(), an error code EINVAL will be returned.
Fixes: f38d138a7da6 ("net/ethtool: support set coalesce per queue")
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Reviewed-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-10-08 23:49:35 +08:00
|
|
|
void __user *useraddr, u32 sub_cmd)
|
2016-02-19 22:24:01 +08:00
|
|
|
{
|
|
|
|
struct ethtool_per_queue_op per_queue_opt;
|
|
|
|
|
|
|
|
if (copy_from_user(&per_queue_opt, useraddr, sizeof(per_queue_opt)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
ethtool: fix a privilege escalation bug
In dev_ethtool(), the eth command 'ethcmd' is firstly copied from the
use-space buffer 'useraddr' and checked to see whether it is
ETHTOOL_PERQUEUE. If yes, the sub-command 'sub_cmd' is further copied from
the user space. Otherwise, 'sub_cmd' is the same as 'ethcmd'. Next,
according to 'sub_cmd', a permission check is enforced through the function
ns_capable(). For example, the permission check is required if 'sub_cmd' is
ETHTOOL_SCOALESCE, but it is not necessary if 'sub_cmd' is
ETHTOOL_GCOALESCE, as suggested in the comment "Allow some commands to be
done by anyone". The following execution invokes different handlers
according to 'ethcmd'. Specifically, if 'ethcmd' is ETHTOOL_PERQUEUE,
ethtool_set_per_queue() is called. In ethtool_set_per_queue(), the kernel
object 'per_queue_opt' is copied again from the user-space buffer
'useraddr' and 'per_queue_opt.sub_command' is used to determine which
operation should be performed. Given that the buffer 'useraddr' is in the
user space, a malicious user can race to change the sub-command between the
two copies. In particular, the attacker can supply ETHTOOL_PERQUEUE and
ETHTOOL_GCOALESCE to bypass the permission check in dev_ethtool(). Then
before ethtool_set_per_queue() is called, the attacker changes
ETHTOOL_GCOALESCE to ETHTOOL_SCOALESCE. In this way, the attacker can
bypass the permission check and execute ETHTOOL_SCOALESCE.
This patch enforces a check in ethtool_set_per_queue() after the second
copy from 'useraddr'. If the sub-command is different from the one obtained
in the first copy in dev_ethtool(), an error code EINVAL will be returned.
Fixes: f38d138a7da6 ("net/ethtool: support set coalesce per queue")
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Reviewed-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-10-08 23:49:35 +08:00
|
|
|
if (per_queue_opt.sub_command != sub_cmd)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-02-19 22:24:01 +08:00
|
|
|
switch (per_queue_opt.sub_command) {
|
2016-02-19 22:24:02 +08:00
|
|
|
case ETHTOOL_GCOALESCE:
|
|
|
|
return ethtool_get_per_queue_coalesce(dev, useraddr, &per_queue_opt);
|
2016-02-19 22:24:03 +08:00
|
|
|
case ETHTOOL_SCOALESCE:
|
|
|
|
return ethtool_set_per_queue_coalesce(dev, useraddr, &per_queue_opt);
|
2016-02-19 22:24:01 +08:00
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-11-17 20:07:21 +08:00
|
|
|
static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna)
|
|
|
|
{
|
|
|
|
switch (tuna->id) {
|
2016-11-17 20:07:23 +08:00
|
|
|
case ETHTOOL_PHY_DOWNSHIFT:
|
2019-03-26 02:34:58 +08:00
|
|
|
case ETHTOOL_PHY_FAST_LINK_DOWN:
|
2016-11-17 20:07:23 +08:00
|
|
|
if (tuna->len != sizeof(u8) ||
|
|
|
|
tuna->type_id != ETHTOOL_TUNABLE_U8)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
ethtool: implement Energy Detect Powerdown support via phy-tunable
The `phy_tunable_id` has been named `ETHTOOL_PHY_EDPD` since it looks like
this feature is common across other PHYs (like EEE), and defining
`ETHTOOL_PHY_ENERGY_DETECT_POWER_DOWN` seems too long.
The way EDPD works, is that the RX block is put to a lower power mode,
except for link-pulse detection circuits. The TX block is also put to low
power mode, but the PHY wakes-up periodically to send link pulses, to avoid
lock-ups in case the other side is also in EDPD mode.
Currently, there are 2 PHY drivers that look like they could use this new
PHY tunable feature: the `adin` && `micrel` PHYs.
The ADIN's datasheet mentions that TX pulses are at intervals of 1 second
default each, and they can be disabled. For the Micrel KSZ9031 PHY, the
datasheet does not mention whether they can be disabled, but mentions that
they can modified.
The way this change is structured, is similar to the PHY tunable downshift
control:
* a `ETHTOOL_PHY_EDPD_DFLT_TX_MSECS` value is exposed to cover a default
TX interval; some PHYs could specify a certain value that makes sense
* `ETHTOOL_PHY_EDPD_NO_TX` would disable TX when EDPD is enabled
* `ETHTOOL_PHY_EDPD_DISABLE` will disable EDPD
As noted by the `ETHTOOL_PHY_EDPD_DFLT_TX_MSECS` the interval unit is 1
millisecond, which should cover a reasonable range of intervals:
- from 1 millisecond, which does not sound like much of a power-saver
- to ~65 seconds which is quite a lot to wait for a link to come up when
plugging a cable
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-09-16 15:35:25 +08:00
|
|
|
case ETHTOOL_PHY_EDPD:
|
|
|
|
if (tuna->len != sizeof(u16) ||
|
|
|
|
tuna->type_id != ETHTOOL_TUNABLE_U16)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
2016-11-17 20:07:21 +08:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_phy_tunable(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ethtool_tunable tuna;
|
|
|
|
struct phy_device *phydev = dev->phydev;
|
|
|
|
void *data;
|
|
|
|
|
|
|
|
if (!(phydev && phydev->drv && phydev->drv->get_tunable))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
|
|
|
|
return -EFAULT;
|
|
|
|
ret = ethtool_phy_tunable_valid(&tuna);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
data = kmalloc(tuna.len, GFP_USER);
|
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
2016-11-23 05:55:31 +08:00
|
|
|
mutex_lock(&phydev->lock);
|
2016-11-17 20:07:21 +08:00
|
|
|
ret = phydev->drv->get_tunable(phydev, &tuna, data);
|
2016-11-23 05:55:31 +08:00
|
|
|
mutex_unlock(&phydev->lock);
|
2016-11-17 20:07:21 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
useraddr += sizeof(tuna);
|
|
|
|
ret = -EFAULT;
|
|
|
|
if (copy_to_user(useraddr, data, tuna.len))
|
|
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_phy_tunable(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ethtool_tunable tuna;
|
|
|
|
struct phy_device *phydev = dev->phydev;
|
|
|
|
void *data;
|
|
|
|
|
|
|
|
if (!(phydev && phydev->drv && phydev->drv->set_tunable))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
|
|
|
|
return -EFAULT;
|
|
|
|
ret = ethtool_phy_tunable_valid(&tuna);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
useraddr += sizeof(tuna);
|
2017-05-14 06:31:26 +08:00
|
|
|
data = memdup_user(useraddr, tuna.len);
|
|
|
|
if (IS_ERR(data))
|
|
|
|
return PTR_ERR(data);
|
2016-11-23 05:55:31 +08:00
|
|
|
mutex_lock(&phydev->lock);
|
2016-11-17 20:07:21 +08:00
|
|
|
ret = phydev->drv->set_tunable(phydev, &tuna, data);
|
2016-11-23 05:55:31 +08:00
|
|
|
mutex_unlock(&phydev->lock);
|
2016-11-17 20:07:21 +08:00
|
|
|
|
|
|
|
kfree(data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-28 07:47:26 +08:00
|
|
|
static int ethtool_get_fecparam(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
2019-02-27 20:47:57 +08:00
|
|
|
struct ethtool_fecparam fecparam = { .cmd = ETHTOOL_GFECPARAM };
|
2018-03-01 03:15:58 +08:00
|
|
|
int rc;
|
2017-07-28 07:47:26 +08:00
|
|
|
|
|
|
|
if (!dev->ethtool_ops->get_fecparam)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2018-03-01 03:15:58 +08:00
|
|
|
rc = dev->ethtool_ops->get_fecparam(dev, &fecparam);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2017-07-28 07:47:26 +08:00
|
|
|
|
|
|
|
if (copy_to_user(useraddr, &fecparam, sizeof(fecparam)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr)
|
|
|
|
{
|
|
|
|
struct ethtool_fecparam fecparam;
|
|
|
|
|
|
|
|
if (!dev->ethtool_ops->set_fecparam)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (copy_from_user(&fecparam, useraddr, sizeof(fecparam)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return dev->ethtool_ops->set_fecparam(dev, &fecparam);
|
|
|
|
}
|
|
|
|
|
2013-06-03 10:03:34 +08:00
|
|
|
/* The main entry point in this file. Called from net/core/dev_ioctl.c */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-09-18 02:56:21 +08:00
|
|
|
int dev_ethtool(struct net *net, struct ifreq *ifr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-09-18 02:56:21 +08:00
|
|
|
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
|
2005-04-17 06:20:36 +08:00
|
|
|
void __user *useraddr = ifr->ifr_data;
|
2016-02-19 22:24:01 +08:00
|
|
|
u32 ethcmd, sub_cmd;
|
2005-04-17 06:20:36 +08:00
|
|
|
int rc;
|
2013-05-02 07:06:42 +08:00
|
|
|
netdev_features_t old_features;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!dev || !netif_device_present(dev))
|
|
|
|
return -ENODEV;
|
|
|
|
|
2010-04-08 12:54:42 +08:00
|
|
|
if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
2016-02-19 22:24:01 +08:00
|
|
|
if (ethcmd == ETHTOOL_PERQUEUE) {
|
|
|
|
if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd)))
|
|
|
|
return -EFAULT;
|
|
|
|
} else {
|
|
|
|
sub_cmd = ethcmd;
|
|
|
|
}
|
2006-09-29 06:13:37 +08:00
|
|
|
/* Allow some commands to be done by anyone */
|
2016-02-19 22:24:01 +08:00
|
|
|
switch (sub_cmd) {
|
2010-08-23 18:24:18 +08:00
|
|
|
case ETHTOOL_GSET:
|
2006-09-29 06:13:37 +08:00
|
|
|
case ETHTOOL_GDRVINFO:
|
|
|
|
case ETHTOOL_GMSGLVL:
|
2012-06-12 21:05:41 +08:00
|
|
|
case ETHTOOL_GLINK:
|
2006-09-29 06:13:37 +08:00
|
|
|
case ETHTOOL_GCOALESCE:
|
|
|
|
case ETHTOOL_GRINGPARAM:
|
|
|
|
case ETHTOOL_GPAUSEPARAM:
|
|
|
|
case ETHTOOL_GRXCSUM:
|
|
|
|
case ETHTOOL_GTXCSUM:
|
|
|
|
case ETHTOOL_GSG:
|
2012-01-22 08:20:40 +08:00
|
|
|
case ETHTOOL_GSSET_INFO:
|
2006-09-29 06:13:37 +08:00
|
|
|
case ETHTOOL_GSTRINGS:
|
2012-06-12 21:05:41 +08:00
|
|
|
case ETHTOOL_GSTATS:
|
2015-12-30 23:28:25 +08:00
|
|
|
case ETHTOOL_GPHYSTATS:
|
2006-09-29 06:13:37 +08:00
|
|
|
case ETHTOOL_GTSO:
|
|
|
|
case ETHTOOL_GPERMADDR:
|
2018-09-22 16:34:01 +08:00
|
|
|
case ETHTOOL_GUFO:
|
2006-09-29 06:13:37 +08:00
|
|
|
case ETHTOOL_GGSO:
|
2010-02-11 21:48:29 +08:00
|
|
|
case ETHTOOL_GGRO:
|
2007-08-16 07:01:32 +08:00
|
|
|
case ETHTOOL_GFLAGS:
|
|
|
|
case ETHTOOL_GPFLAGS:
|
2008-07-02 18:47:41 +08:00
|
|
|
case ETHTOOL_GRXFH:
|
2009-02-20 16:58:13 +08:00
|
|
|
case ETHTOOL_GRXRINGS:
|
|
|
|
case ETHTOOL_GRXCLSRLCNT:
|
|
|
|
case ETHTOOL_GRXCLSRULE:
|
|
|
|
case ETHTOOL_GRXCLSRLALL:
|
2012-06-12 21:05:41 +08:00
|
|
|
case ETHTOOL_GRXFHINDIR:
|
2014-04-21 18:07:59 +08:00
|
|
|
case ETHTOOL_GRSSH:
|
2011-02-16 00:59:17 +08:00
|
|
|
case ETHTOOL_GFEATURES:
|
2012-06-12 21:05:41 +08:00
|
|
|
case ETHTOOL_GCHANNELS:
|
2012-04-04 06:59:17 +08:00
|
|
|
case ETHTOOL_GET_TS_INFO:
|
2012-06-12 21:05:41 +08:00
|
|
|
case ETHTOOL_GEEE:
|
2014-09-03 05:47:20 +08:00
|
|
|
case ETHTOOL_GTUNABLE:
|
2016-11-17 20:07:21 +08:00
|
|
|
case ETHTOOL_PHY_GTUNABLE:
|
2016-11-24 17:55:06 +08:00
|
|
|
case ETHTOOL_GLINKSETTINGS:
|
2017-07-28 07:47:26 +08:00
|
|
|
case ETHTOOL_GFECPARAM:
|
2006-09-29 06:13:37 +08:00
|
|
|
break;
|
|
|
|
default:
|
2012-11-16 11:03:04 +08:00
|
|
|
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
2006-09-29 06:13:37 +08:00
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
2010-04-08 12:54:42 +08:00
|
|
|
if (dev->ethtool_ops->begin) {
|
|
|
|
rc = dev->ethtool_ops->begin(dev);
|
|
|
|
if (rc < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return rc;
|
2010-04-08 12:54:42 +08:00
|
|
|
}
|
2005-05-30 05:13:47 +08:00
|
|
|
old_features = dev->features;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (ethcmd) {
|
|
|
|
case ETHTOOL_GSET:
|
|
|
|
rc = ethtool_get_settings(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SSET:
|
|
|
|
rc = ethtool_set_settings(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GDRVINFO:
|
|
|
|
rc = ethtool_get_drvinfo(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GREGS:
|
|
|
|
rc = ethtool_get_regs(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GWOL:
|
|
|
|
rc = ethtool_get_wol(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SWOL:
|
|
|
|
rc = ethtool_set_wol(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GMSGLVL:
|
2007-08-16 07:01:56 +08:00
|
|
|
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
|
|
|
dev->ethtool_ops->get_msglevel);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case ETHTOOL_SMSGLVL:
|
2007-08-16 07:01:56 +08:00
|
|
|
rc = ethtool_set_value_void(dev, useraddr,
|
|
|
|
dev->ethtool_ops->set_msglevel);
|
2020-01-27 06:11:10 +08:00
|
|
|
if (!rc)
|
|
|
|
ethtool_notify(dev, ETHTOOL_MSG_DEBUG_NTF, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2012-06-07 01:13:06 +08:00
|
|
|
case ETHTOOL_GEEE:
|
|
|
|
rc = ethtool_get_eee(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SEEE:
|
|
|
|
rc = ethtool_set_eee(dev, useraddr);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
case ETHTOOL_NWAY_RST:
|
|
|
|
rc = ethtool_nway_reset(dev);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GLINK:
|
2010-12-09 20:08:35 +08:00
|
|
|
rc = ethtool_get_link(dev, useraddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case ETHTOOL_GEEPROM:
|
|
|
|
rc = ethtool_get_eeprom(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SEEPROM:
|
|
|
|
rc = ethtool_set_eeprom(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GCOALESCE:
|
|
|
|
rc = ethtool_get_coalesce(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SCOALESCE:
|
|
|
|
rc = ethtool_set_coalesce(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GRINGPARAM:
|
|
|
|
rc = ethtool_get_ringparam(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SRINGPARAM:
|
|
|
|
rc = ethtool_set_ringparam(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GPAUSEPARAM:
|
|
|
|
rc = ethtool_get_pauseparam(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SPAUSEPARAM:
|
|
|
|
rc = ethtool_set_pauseparam(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_TEST:
|
|
|
|
rc = ethtool_self_test(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GSTRINGS:
|
|
|
|
rc = ethtool_get_strings(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_PHYS_ID:
|
|
|
|
rc = ethtool_phys_id(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GSTATS:
|
|
|
|
rc = ethtool_get_stats(dev, useraddr);
|
|
|
|
break;
|
2005-08-21 08:15:54 +08:00
|
|
|
case ETHTOOL_GPERMADDR:
|
|
|
|
rc = ethtool_get_perm_addr(dev, useraddr);
|
|
|
|
break;
|
2007-08-16 07:00:51 +08:00
|
|
|
case ETHTOOL_GFLAGS:
|
2007-08-16 07:01:56 +08:00
|
|
|
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
2011-11-15 23:29:55 +08:00
|
|
|
__ethtool_get_flags);
|
2007-08-16 07:00:51 +08:00
|
|
|
break;
|
|
|
|
case ETHTOOL_SFLAGS:
|
2011-02-16 00:59:18 +08:00
|
|
|
rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags);
|
2007-08-16 07:00:51 +08:00
|
|
|
break;
|
2007-08-16 07:01:32 +08:00
|
|
|
case ETHTOOL_GPFLAGS:
|
2007-08-16 07:01:56 +08:00
|
|
|
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
|
|
|
dev->ethtool_ops->get_priv_flags);
|
2007-08-16 07:01:32 +08:00
|
|
|
break;
|
|
|
|
case ETHTOOL_SPFLAGS:
|
2007-08-16 07:01:56 +08:00
|
|
|
rc = ethtool_set_value(dev, useraddr,
|
|
|
|
dev->ethtool_ops->set_priv_flags);
|
2007-08-16 07:01:32 +08:00
|
|
|
break;
|
2008-07-02 18:47:41 +08:00
|
|
|
case ETHTOOL_GRXFH:
|
2009-02-20 16:58:13 +08:00
|
|
|
case ETHTOOL_GRXRINGS:
|
|
|
|
case ETHTOOL_GRXCLSRLCNT:
|
|
|
|
case ETHTOOL_GRXCLSRULE:
|
|
|
|
case ETHTOOL_GRXCLSRLALL:
|
2010-06-28 16:45:58 +08:00
|
|
|
rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);
|
2008-07-02 18:47:41 +08:00
|
|
|
break;
|
|
|
|
case ETHTOOL_SRXFH:
|
2009-02-20 16:58:13 +08:00
|
|
|
case ETHTOOL_SRXCLSRLDEL:
|
|
|
|
case ETHTOOL_SRXCLSRLINS:
|
2010-06-28 16:45:58 +08:00
|
|
|
rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
|
2008-07-02 18:47:41 +08:00
|
|
|
break;
|
2009-09-03 01:02:55 +08:00
|
|
|
case ETHTOOL_FLASHDEV:
|
|
|
|
rc = ethtool_flash_device(dev, useraddr);
|
|
|
|
break;
|
2009-10-05 18:59:58 +08:00
|
|
|
case ETHTOOL_RESET:
|
|
|
|
rc = ethtool_reset(dev, useraddr);
|
|
|
|
break;
|
2010-03-04 06:51:50 +08:00
|
|
|
case ETHTOOL_GSSET_INFO:
|
|
|
|
rc = ethtool_get_sset_info(dev, useraddr);
|
|
|
|
break;
|
2010-06-30 13:05:23 +08:00
|
|
|
case ETHTOOL_GRXFHINDIR:
|
|
|
|
rc = ethtool_get_rxfh_indir(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SRXFHINDIR:
|
|
|
|
rc = ethtool_set_rxfh_indir(dev, useraddr);
|
|
|
|
break;
|
2014-04-21 18:07:59 +08:00
|
|
|
case ETHTOOL_GRSSH:
|
|
|
|
rc = ethtool_get_rxfh(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SRSSH:
|
|
|
|
rc = ethtool_set_rxfh(dev, useraddr);
|
|
|
|
break;
|
2011-02-16 00:59:17 +08:00
|
|
|
case ETHTOOL_GFEATURES:
|
|
|
|
rc = ethtool_get_features(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SFEATURES:
|
|
|
|
rc = ethtool_set_features(dev, useraddr);
|
|
|
|
break;
|
2011-02-16 00:59:17 +08:00
|
|
|
case ETHTOOL_GTXCSUM:
|
2011-02-16 00:59:18 +08:00
|
|
|
case ETHTOOL_GRXCSUM:
|
2011-02-16 00:59:17 +08:00
|
|
|
case ETHTOOL_GSG:
|
|
|
|
case ETHTOOL_GTSO:
|
|
|
|
case ETHTOOL_GGSO:
|
|
|
|
case ETHTOOL_GGRO:
|
|
|
|
rc = ethtool_get_one_feature(dev, useraddr, ethcmd);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_STXCSUM:
|
2011-02-16 00:59:18 +08:00
|
|
|
case ETHTOOL_SRXCSUM:
|
2011-02-16 00:59:17 +08:00
|
|
|
case ETHTOOL_SSG:
|
|
|
|
case ETHTOOL_STSO:
|
|
|
|
case ETHTOOL_SGSO:
|
|
|
|
case ETHTOOL_SGRO:
|
|
|
|
rc = ethtool_set_one_feature(dev, useraddr, ethcmd);
|
|
|
|
break;
|
2011-04-07 09:58:42 +08:00
|
|
|
case ETHTOOL_GCHANNELS:
|
|
|
|
rc = ethtool_get_channels(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SCHANNELS:
|
|
|
|
rc = ethtool_set_channels(dev, useraddr);
|
|
|
|
break;
|
2011-05-12 20:48:32 +08:00
|
|
|
case ETHTOOL_SET_DUMP:
|
|
|
|
rc = ethtool_set_dump(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GET_DUMP_FLAG:
|
|
|
|
rc = ethtool_get_dump_flag(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GET_DUMP_DATA:
|
|
|
|
rc = ethtool_get_dump_data(dev, useraddr);
|
|
|
|
break;
|
2012-04-04 06:59:17 +08:00
|
|
|
case ETHTOOL_GET_TS_INFO:
|
|
|
|
rc = ethtool_get_ts_info(dev, useraddr);
|
|
|
|
break;
|
2012-04-19 16:44:42 +08:00
|
|
|
case ETHTOOL_GMODULEINFO:
|
|
|
|
rc = ethtool_get_module_info(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_GMODULEEEPROM:
|
|
|
|
rc = ethtool_get_module_eeprom(dev, useraddr);
|
|
|
|
break;
|
2014-09-03 05:47:20 +08:00
|
|
|
case ETHTOOL_GTUNABLE:
|
|
|
|
rc = ethtool_get_tunable(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_STUNABLE:
|
|
|
|
rc = ethtool_set_tunable(dev, useraddr);
|
|
|
|
break;
|
2015-12-30 23:28:25 +08:00
|
|
|
case ETHTOOL_GPHYSTATS:
|
|
|
|
rc = ethtool_get_phy_stats(dev, useraddr);
|
|
|
|
break;
|
2016-02-19 22:24:01 +08:00
|
|
|
case ETHTOOL_PERQUEUE:
|
ethtool: fix a privilege escalation bug
In dev_ethtool(), the eth command 'ethcmd' is firstly copied from the
use-space buffer 'useraddr' and checked to see whether it is
ETHTOOL_PERQUEUE. If yes, the sub-command 'sub_cmd' is further copied from
the user space. Otherwise, 'sub_cmd' is the same as 'ethcmd'. Next,
according to 'sub_cmd', a permission check is enforced through the function
ns_capable(). For example, the permission check is required if 'sub_cmd' is
ETHTOOL_SCOALESCE, but it is not necessary if 'sub_cmd' is
ETHTOOL_GCOALESCE, as suggested in the comment "Allow some commands to be
done by anyone". The following execution invokes different handlers
according to 'ethcmd'. Specifically, if 'ethcmd' is ETHTOOL_PERQUEUE,
ethtool_set_per_queue() is called. In ethtool_set_per_queue(), the kernel
object 'per_queue_opt' is copied again from the user-space buffer
'useraddr' and 'per_queue_opt.sub_command' is used to determine which
operation should be performed. Given that the buffer 'useraddr' is in the
user space, a malicious user can race to change the sub-command between the
two copies. In particular, the attacker can supply ETHTOOL_PERQUEUE and
ETHTOOL_GCOALESCE to bypass the permission check in dev_ethtool(). Then
before ethtool_set_per_queue() is called, the attacker changes
ETHTOOL_GCOALESCE to ETHTOOL_SCOALESCE. In this way, the attacker can
bypass the permission check and execute ETHTOOL_SCOALESCE.
This patch enforces a check in ethtool_set_per_queue() after the second
copy from 'useraddr'. If the sub-command is different from the one obtained
in the first copy in dev_ethtool(), an error code EINVAL will be returned.
Fixes: f38d138a7da6 ("net/ethtool: support set coalesce per queue")
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Reviewed-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-10-08 23:49:35 +08:00
|
|
|
rc = ethtool_set_per_queue(dev, useraddr, sub_cmd);
|
2016-02-19 22:24:01 +08:00
|
|
|
break;
|
net: ethtool: add new ETHTOOL_xLINKSETTINGS API
This patch defines a new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API,
handled by the new get_link_ksettings/set_link_ksettings callbacks.
This API provides support for most legacy ethtool_cmd fields, adds
support for larger link mode masks (up to 4064 bits, variable length),
and removes ethtool_cmd deprecated
fields (transceiver/maxrxpkt/maxtxpkt).
This API is deprecating the legacy ETHTOOL_GSET/SSET API and provides
the following backward compatibility properties:
- legacy ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks.
- legacy ethtool with new get/set_link_ksettings drivers: the new
driver callbacks are used, data internally converted to legacy
ethtool_cmd. ETHTOOL_GSET will return only the 1st 32b of each link
mode mask. ETHTOOL_SSET will fail if user tries to set the
ethtool_cmd deprecated fields to
non-0 (transceiver/maxrxpkt/maxtxpkt). A kernel warning is logged if
driver sets higher bits.
- future ethtool with legacy drivers: no change, still using the
get_settings/set_settings callbacks, internally converted to new data
structure. Deprecated fields (transceiver/maxrxpkt/maxtxpkt) will be
ignored and seen as 0 from user space. Note that that "future"
ethtool tool will not allow changes to these deprecated fields.
- future ethtool with new drivers: direct call to the new callbacks.
By "future" ethtool, what is meant is:
- query: first try ETHTOOL_GLINKSETTINGS, and revert to ETHTOOL_GSET if
fails
- set: query first and remember which of ETHTOOL_GLINKSETTINGS or
ETHTOOL_GSET was successful
+ if ETHTOOL_GLINKSETTINGS was successful, then change config with
ETHTOOL_SLINKSETTINGS. A failure there is final (do not try
ETHTOOL_SSET).
+ otherwise ETHTOOL_GSET was successful, change config with
ETHTOOL_SSET. A failure there is final (do not try
ETHTOOL_SLINKSETTINGS).
The interaction user/kernel via the new API requires a small
ETHTOOL_GLINKSETTINGS handshake first to agree on the length of the link
mode bitmaps. If kernel doesn't agree with user, it returns the bitmap
length it is expecting from user as a negative length (and cmd field is
0). When kernel and user agree, kernel returns valid info in all
fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS).
Data structure crossing user/kernel boundary is 32/64-bit
agnostic. Converted internally to a legal kernel bitmap.
The internal __ethtool_get_settings kernel helper will gradually be
replaced by __ethtool_get_link_ksettings by the time the first
"link_settings" drivers start to appear. So this patch doesn't change
it, it will be removed before it needs to be changed.
Signed-off-by: David Decotigny <decot@googlers.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-02-25 02:57:59 +08:00
|
|
|
case ETHTOOL_GLINKSETTINGS:
|
|
|
|
rc = ethtool_get_link_ksettings(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SLINKSETTINGS:
|
|
|
|
rc = ethtool_set_link_ksettings(dev, useraddr);
|
|
|
|
break;
|
2016-11-17 20:07:21 +08:00
|
|
|
case ETHTOOL_PHY_GTUNABLE:
|
|
|
|
rc = get_phy_tunable(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_PHY_STUNABLE:
|
|
|
|
rc = set_phy_tunable(dev, useraddr);
|
|
|
|
break;
|
2017-07-28 07:47:26 +08:00
|
|
|
case ETHTOOL_GFECPARAM:
|
|
|
|
rc = ethtool_get_fecparam(dev, useraddr);
|
|
|
|
break;
|
|
|
|
case ETHTOOL_SFECPARAM:
|
|
|
|
rc = ethtool_set_fecparam(dev, useraddr);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
2007-08-01 05:00:02 +08:00
|
|
|
rc = -EOPNOTSUPP;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-02-09 22:24:36 +08:00
|
|
|
|
2007-04-11 11:10:33 +08:00
|
|
|
if (dev->ethtool_ops->complete)
|
2005-04-17 06:20:36 +08:00
|
|
|
dev->ethtool_ops->complete(dev);
|
2005-05-30 05:13:47 +08:00
|
|
|
|
|
|
|
if (old_features != dev->features)
|
|
|
|
netdev_features_change(dev);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return rc;
|
|
|
|
}
|
2019-02-02 19:50:51 +08:00
|
|
|
|
|
|
|
struct ethtool_rx_flow_key {
|
|
|
|
struct flow_dissector_key_basic basic;
|
|
|
|
union {
|
|
|
|
struct flow_dissector_key_ipv4_addrs ipv4;
|
|
|
|
struct flow_dissector_key_ipv6_addrs ipv6;
|
|
|
|
};
|
|
|
|
struct flow_dissector_key_ports tp;
|
|
|
|
struct flow_dissector_key_ip ip;
|
|
|
|
struct flow_dissector_key_vlan vlan;
|
|
|
|
struct flow_dissector_key_eth_addrs eth_addrs;
|
|
|
|
} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
|
|
|
|
|
|
|
|
struct ethtool_rx_flow_match {
|
|
|
|
struct flow_dissector dissector;
|
|
|
|
struct ethtool_rx_flow_key key;
|
|
|
|
struct ethtool_rx_flow_key mask;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ethtool_rx_flow_rule *
|
|
|
|
ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input)
|
|
|
|
{
|
|
|
|
const struct ethtool_rx_flow_spec *fs = input->fs;
|
|
|
|
static struct in6_addr zero_addr = {};
|
|
|
|
struct ethtool_rx_flow_match *match;
|
|
|
|
struct ethtool_rx_flow_rule *flow;
|
|
|
|
struct flow_action_entry *act;
|
|
|
|
|
|
|
|
flow = kzalloc(sizeof(struct ethtool_rx_flow_rule) +
|
|
|
|
sizeof(struct ethtool_rx_flow_match), GFP_KERNEL);
|
|
|
|
if (!flow)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
/* ethtool_rx supports only one single action per rule. */
|
|
|
|
flow->rule = flow_rule_alloc(1);
|
|
|
|
if (!flow->rule) {
|
|
|
|
kfree(flow);
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
match = (struct ethtool_rx_flow_match *)flow->priv;
|
|
|
|
flow->rule->match.dissector = &match->dissector;
|
|
|
|
flow->rule->match.mask = &match->mask;
|
|
|
|
flow->rule->match.key = &match->key;
|
|
|
|
|
|
|
|
match->mask.basic.n_proto = htons(0xffff);
|
|
|
|
|
|
|
|
switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
|
2019-06-27 16:52:26 +08:00
|
|
|
case ETHER_FLOW: {
|
|
|
|
const struct ethhdr *ether_spec, *ether_m_spec;
|
|
|
|
|
|
|
|
ether_spec = &fs->h_u.ether_spec;
|
|
|
|
ether_m_spec = &fs->m_u.ether_spec;
|
|
|
|
|
|
|
|
if (!is_zero_ether_addr(ether_m_spec->h_source)) {
|
|
|
|
ether_addr_copy(match->key.eth_addrs.src,
|
|
|
|
ether_spec->h_source);
|
|
|
|
ether_addr_copy(match->mask.eth_addrs.src,
|
|
|
|
ether_m_spec->h_source);
|
|
|
|
}
|
|
|
|
if (!is_zero_ether_addr(ether_m_spec->h_dest)) {
|
|
|
|
ether_addr_copy(match->key.eth_addrs.dst,
|
|
|
|
ether_spec->h_dest);
|
|
|
|
ether_addr_copy(match->mask.eth_addrs.dst,
|
|
|
|
ether_m_spec->h_dest);
|
|
|
|
}
|
|
|
|
if (ether_m_spec->h_proto) {
|
|
|
|
match->key.basic.n_proto = ether_spec->h_proto;
|
|
|
|
match->mask.basic.n_proto = ether_m_spec->h_proto;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2019-02-02 19:50:51 +08:00
|
|
|
case TCP_V4_FLOW:
|
|
|
|
case UDP_V4_FLOW: {
|
|
|
|
const struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec;
|
|
|
|
|
|
|
|
match->key.basic.n_proto = htons(ETH_P_IP);
|
|
|
|
|
|
|
|
v4_spec = &fs->h_u.tcp_ip4_spec;
|
|
|
|
v4_m_spec = &fs->m_u.tcp_ip4_spec;
|
|
|
|
|
|
|
|
if (v4_m_spec->ip4src) {
|
|
|
|
match->key.ipv4.src = v4_spec->ip4src;
|
|
|
|
match->mask.ipv4.src = v4_m_spec->ip4src;
|
|
|
|
}
|
|
|
|
if (v4_m_spec->ip4dst) {
|
|
|
|
match->key.ipv4.dst = v4_spec->ip4dst;
|
|
|
|
match->mask.ipv4.dst = v4_m_spec->ip4dst;
|
|
|
|
}
|
|
|
|
if (v4_m_spec->ip4src ||
|
|
|
|
v4_m_spec->ip4dst) {
|
|
|
|
match->dissector.used_keys |=
|
|
|
|
BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_IPV4_ADDRS] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, ipv4);
|
|
|
|
}
|
|
|
|
if (v4_m_spec->psrc) {
|
|
|
|
match->key.tp.src = v4_spec->psrc;
|
|
|
|
match->mask.tp.src = v4_m_spec->psrc;
|
|
|
|
}
|
|
|
|
if (v4_m_spec->pdst) {
|
|
|
|
match->key.tp.dst = v4_spec->pdst;
|
|
|
|
match->mask.tp.dst = v4_m_spec->pdst;
|
|
|
|
}
|
|
|
|
if (v4_m_spec->psrc ||
|
|
|
|
v4_m_spec->pdst) {
|
|
|
|
match->dissector.used_keys |=
|
|
|
|
BIT(FLOW_DISSECTOR_KEY_PORTS);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, tp);
|
|
|
|
}
|
|
|
|
if (v4_m_spec->tos) {
|
|
|
|
match->key.ip.tos = v4_spec->tos;
|
|
|
|
match->mask.ip.tos = v4_m_spec->tos;
|
|
|
|
match->dissector.used_keys |=
|
|
|
|
BIT(FLOW_DISSECTOR_KEY_IP);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, ip);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TCP_V6_FLOW:
|
|
|
|
case UDP_V6_FLOW: {
|
|
|
|
const struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec;
|
|
|
|
|
|
|
|
match->key.basic.n_proto = htons(ETH_P_IPV6);
|
|
|
|
|
|
|
|
v6_spec = &fs->h_u.tcp_ip6_spec;
|
|
|
|
v6_m_spec = &fs->m_u.tcp_ip6_spec;
|
|
|
|
if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
|
|
|
|
memcpy(&match->key.ipv6.src, v6_spec->ip6src,
|
|
|
|
sizeof(match->key.ipv6.src));
|
|
|
|
memcpy(&match->mask.ipv6.src, v6_m_spec->ip6src,
|
|
|
|
sizeof(match->mask.ipv6.src));
|
|
|
|
}
|
|
|
|
if (memcmp(v6_m_spec->ip6dst, &zero_addr, sizeof(zero_addr))) {
|
|
|
|
memcpy(&match->key.ipv6.dst, v6_spec->ip6dst,
|
|
|
|
sizeof(match->key.ipv6.dst));
|
|
|
|
memcpy(&match->mask.ipv6.dst, v6_m_spec->ip6dst,
|
|
|
|
sizeof(match->mask.ipv6.dst));
|
|
|
|
}
|
|
|
|
if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr)) ||
|
|
|
|
memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
|
|
|
|
match->dissector.used_keys |=
|
|
|
|
BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_IPV6_ADDRS] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, ipv6);
|
|
|
|
}
|
|
|
|
if (v6_m_spec->psrc) {
|
|
|
|
match->key.tp.src = v6_spec->psrc;
|
|
|
|
match->mask.tp.src = v6_m_spec->psrc;
|
|
|
|
}
|
|
|
|
if (v6_m_spec->pdst) {
|
|
|
|
match->key.tp.dst = v6_spec->pdst;
|
|
|
|
match->mask.tp.dst = v6_m_spec->pdst;
|
|
|
|
}
|
|
|
|
if (v6_m_spec->psrc ||
|
|
|
|
v6_m_spec->pdst) {
|
|
|
|
match->dissector.used_keys |=
|
|
|
|
BIT(FLOW_DISSECTOR_KEY_PORTS);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, tp);
|
|
|
|
}
|
|
|
|
if (v6_m_spec->tclass) {
|
|
|
|
match->key.ip.tos = v6_spec->tclass;
|
|
|
|
match->mask.ip.tos = v6_m_spec->tclass;
|
|
|
|
match->dissector.used_keys |=
|
|
|
|
BIT(FLOW_DISSECTOR_KEY_IP);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, ip);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ethtool_rx_flow_rule_destroy(flow);
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
|
|
|
|
case TCP_V4_FLOW:
|
|
|
|
case TCP_V6_FLOW:
|
|
|
|
match->key.basic.ip_proto = IPPROTO_TCP;
|
|
|
|
break;
|
|
|
|
case UDP_V4_FLOW:
|
|
|
|
case UDP_V6_FLOW:
|
|
|
|
match->key.basic.ip_proto = IPPROTO_UDP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
match->mask.basic.ip_proto = 0xff;
|
|
|
|
|
|
|
|
match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_BASIC] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, basic);
|
|
|
|
|
|
|
|
if (fs->flow_type & FLOW_EXT) {
|
|
|
|
const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
|
|
|
|
const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
|
|
|
|
|
2019-05-30 22:08:40 +08:00
|
|
|
if (ext_m_spec->vlan_etype) {
|
2019-02-02 19:50:51 +08:00
|
|
|
match->key.vlan.vlan_tpid = ext_h_spec->vlan_etype;
|
|
|
|
match->mask.vlan.vlan_tpid = ext_m_spec->vlan_etype;
|
2019-05-30 22:08:40 +08:00
|
|
|
}
|
2019-02-02 19:50:51 +08:00
|
|
|
|
2019-05-30 22:08:40 +08:00
|
|
|
if (ext_m_spec->vlan_tci) {
|
2019-02-02 19:50:51 +08:00
|
|
|
match->key.vlan.vlan_id =
|
|
|
|
ntohs(ext_h_spec->vlan_tci) & 0x0fff;
|
|
|
|
match->mask.vlan.vlan_id =
|
|
|
|
ntohs(ext_m_spec->vlan_tci) & 0x0fff;
|
|
|
|
|
2019-06-12 23:18:38 +08:00
|
|
|
match->key.vlan.vlan_dei =
|
|
|
|
!!(ext_h_spec->vlan_tci & htons(0x1000));
|
|
|
|
match->mask.vlan.vlan_dei =
|
|
|
|
!!(ext_m_spec->vlan_tci & htons(0x1000));
|
|
|
|
|
2019-02-02 19:50:51 +08:00
|
|
|
match->key.vlan.vlan_priority =
|
|
|
|
(ntohs(ext_h_spec->vlan_tci) & 0xe000) >> 13;
|
|
|
|
match->mask.vlan.vlan_priority =
|
|
|
|
(ntohs(ext_m_spec->vlan_tci) & 0xe000) >> 13;
|
2019-05-30 22:08:40 +08:00
|
|
|
}
|
2019-02-02 19:50:51 +08:00
|
|
|
|
2019-05-30 22:08:40 +08:00
|
|
|
if (ext_m_spec->vlan_etype ||
|
|
|
|
ext_m_spec->vlan_tci) {
|
2019-02-02 19:50:51 +08:00
|
|
|
match->dissector.used_keys |=
|
|
|
|
BIT(FLOW_DISSECTOR_KEY_VLAN);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, vlan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fs->flow_type & FLOW_MAC_EXT) {
|
|
|
|
const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
|
|
|
|
const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
|
|
|
|
|
2019-02-08 12:46:53 +08:00
|
|
|
memcpy(match->key.eth_addrs.dst, ext_h_spec->h_dest,
|
|
|
|
ETH_ALEN);
|
|
|
|
memcpy(match->mask.eth_addrs.dst, ext_m_spec->h_dest,
|
|
|
|
ETH_ALEN);
|
|
|
|
|
|
|
|
match->dissector.used_keys |=
|
|
|
|
BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
|
|
|
|
match->dissector.offset[FLOW_DISSECTOR_KEY_ETH_ADDRS] =
|
|
|
|
offsetof(struct ethtool_rx_flow_key, eth_addrs);
|
2019-02-02 19:50:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
act = &flow->rule->action.entries[0];
|
|
|
|
switch (fs->ring_cookie) {
|
|
|
|
case RX_CLS_FLOW_DISC:
|
|
|
|
act->id = FLOW_ACTION_DROP;
|
|
|
|
break;
|
|
|
|
case RX_CLS_FLOW_WAKE:
|
|
|
|
act->id = FLOW_ACTION_WAKE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
act->id = FLOW_ACTION_QUEUE;
|
|
|
|
if (fs->flow_type & FLOW_RSS)
|
|
|
|
act->queue.ctx = input->rss_ctx;
|
|
|
|
|
|
|
|
act->queue.vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
|
|
|
|
act->queue.index = ethtool_get_flow_spec_ring(fs->ring_cookie);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return flow;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ethtool_rx_flow_rule_create);
|
|
|
|
|
|
|
|
void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *flow)
|
|
|
|
{
|
|
|
|
kfree(flow->rule);
|
|
|
|
kfree(flow);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ethtool_rx_flow_rule_destroy);
|