blob: 89d0b1827aaff3d454e4467ff7502732f640dc4b [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * net/core/ethtool.c - Ethtool ioctl handler
4 * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
5 *
6 * This file is where we call all the ethtool_ops commands to get
Matthew Wilcox61a44b92007-07-31 14:00:02 -07007 * the information ethtool needs.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 */
9
10#include <linux/module.h>
11#include <linux/types.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080012#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/errno.h>
14#include <linux/ethtool.h>
15#include <linux/netdevice.h>
Richard Cochranc8f3a8c2012-04-03 22:59:17 +000016#include <linux/net_tstamp.h>
17#include <linux/phy.h>
Jeff Garzikd17792e2010-03-04 08:21:53 +000018#include <linux/bitops.h>
chavey97f8aef2010-04-07 21:54:42 -070019#include <linux/uaccess.h>
Leon Romanovsky6a7e25c2020-01-27 09:20:28 +020020#include <linux/vermagic.h>
David S. Miller73da16c2010-09-21 16:12:11 -070021#include <linux/vmalloc.h>
Russell Kinge679c9c2018-03-28 15:44:16 -070022#include <linux/sfp.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090023#include <linux/slab.h>
Ben Hutchings68f512f2011-04-02 00:35:15 +010024#include <linux/rtnetlink.h>
Ingo Molnar174cd4b2017-02-02 19:15:33 +010025#include <linux/sched/signal.h>
Eric Dumazet960fb622014-11-16 06:23:05 -080026#include <linux/net.h>
Jakub Kicinskiddb6e992019-01-31 10:50:47 -080027#include <net/devlink.h>
Jakub Kicinski1661d342018-10-01 14:51:36 +020028#include <net/xdp_sock.h>
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +010029#include <net/flow_offload.h>
Michal Kubecek73286732019-12-27 15:56:03 +010030#include <linux/ethtool_netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Michal Kubecekd44e1312019-12-11 10:58:29 +010032#include "common.h"
33
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090034/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 * Some useful ethtool_ops methods that're device independent.
36 * If we find that all drivers want to do the same thing here,
37 * we can turn these into dev_() function calls.
38 */
39
40u32 ethtool_op_get_link(struct net_device *dev)
41{
42 return netif_carrier_ok(dev) ? 1 : 0;
43}
chavey97f8aef2010-04-07 21:54:42 -070044EXPORT_SYMBOL(ethtool_op_get_link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
Richard Cochran02eacbd2012-04-03 22:59:22 +000046int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
47{
48 info->so_timestamping =
49 SOF_TIMESTAMPING_TX_SOFTWARE |
50 SOF_TIMESTAMPING_RX_SOFTWARE |
51 SOF_TIMESTAMPING_SOFTWARE;
52 info->phc_index = -1;
53 return 0;
54}
55EXPORT_SYMBOL(ethtool_op_get_ts_info);
56
Linus Torvalds1da177e2005-04-16 15:20:36 -070057/* Handlers for each ethtool command */
58
Michał Mirosław5455c692011-02-15 16:59:17 +000059static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
60{
61 struct ethtool_gfeatures cmd = {
62 .cmd = ETHTOOL_GFEATURES,
63 .size = ETHTOOL_DEV_FEATURE_WORDS,
64 };
Michał Mirosław475414f2011-11-15 15:29:55 +000065 struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
Michał Mirosław5455c692011-02-15 16:59:17 +000066 u32 __user *sizeaddr;
67 u32 copy_size;
Michał Mirosław475414f2011-11-15 15:29:55 +000068 int i;
69
70 /* in case feature bits run out again */
Michał Mirosław09da71b2011-11-16 14:32:03 +000071 BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) > sizeof(netdev_features_t));
Michał Mirosław475414f2011-11-15 15:29:55 +000072
73 for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {
Michał Mirosław09da71b2011-11-16 14:32:03 +000074 features[i].available = (u32)(dev->hw_features >> (32 * i));
75 features[i].requested = (u32)(dev->wanted_features >> (32 * i));
76 features[i].active = (u32)(dev->features >> (32 * i));
77 features[i].never_changed =
78 (u32)(NETIF_F_NEVER_CHANGE >> (32 * i));
Michał Mirosław475414f2011-11-15 15:29:55 +000079 }
Michał Mirosław5455c692011-02-15 16:59:17 +000080
81 sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
82 if (get_user(copy_size, sizeaddr))
83 return -EFAULT;
84
85 if (copy_size > ETHTOOL_DEV_FEATURE_WORDS)
86 copy_size = ETHTOOL_DEV_FEATURE_WORDS;
87
88 if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
89 return -EFAULT;
90 useraddr += sizeof(cmd);
91 if (copy_to_user(useraddr, features, copy_size * sizeof(*features)))
92 return -EFAULT;
93
94 return 0;
95}
96
97static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
98{
99 struct ethtool_sfeatures cmd;
100 struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
Michał Mirosław475414f2011-11-15 15:29:55 +0000101 netdev_features_t wanted = 0, valid = 0;
102 int i, ret = 0;
Michał Mirosław5455c692011-02-15 16:59:17 +0000103
104 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
105 return -EFAULT;
106 useraddr += sizeof(cmd);
107
108 if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS)
109 return -EINVAL;
110
111 if (copy_from_user(features, useraddr, sizeof(features)))
112 return -EFAULT;
113
Michał Mirosław475414f2011-11-15 15:29:55 +0000114 for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {
Michał Mirosław09da71b2011-11-16 14:32:03 +0000115 valid |= (netdev_features_t)features[i].valid << (32 * i);
116 wanted |= (netdev_features_t)features[i].requested << (32 * i);
Michał Mirosław475414f2011-11-15 15:29:55 +0000117 }
118
119 if (valid & ~NETIF_F_ETHTOOL_BITS)
Michał Mirosław5455c692011-02-15 16:59:17 +0000120 return -EINVAL;
121
Michał Mirosław475414f2011-11-15 15:29:55 +0000122 if (valid & ~dev->hw_features) {
123 valid &= dev->hw_features;
Michał Mirosław5455c692011-02-15 16:59:17 +0000124 ret |= ETHTOOL_F_UNSUPPORTED;
125 }
126
Michał Mirosław475414f2011-11-15 15:29:55 +0000127 dev->wanted_features &= ~valid;
128 dev->wanted_features |= wanted & valid;
Michał Mirosław6cb6a272011-04-02 22:48:47 -0700129 __netdev_update_features(dev);
Michał Mirosław5455c692011-02-15 16:59:17 +0000130
Michał Mirosław475414f2011-11-15 15:29:55 +0000131 if ((dev->wanted_features ^ dev->features) & valid)
Michał Mirosław5455c692011-02-15 16:59:17 +0000132 ret |= ETHTOOL_F_WISH;
133
134 return ret;
135}
136
Michał Mirosław340ae162011-02-15 16:59:16 +0000137static int __ethtool_get_sset_count(struct net_device *dev, int sset)
138{
139 const struct ethtool_ops *ops = dev->ethtool_ops;
140
Michał Mirosław5455c692011-02-15 16:59:17 +0000141 if (sset == ETH_SS_FEATURES)
142 return ARRAY_SIZE(netdev_features_strings);
143
Eyal Perry892311f2014-12-02 18:12:10 +0200144 if (sset == ETH_SS_RSS_HASH_FUNCS)
145 return ARRAY_SIZE(rss_hash_func_strings);
146
Hadar Hen Ziona4244b02015-06-11 10:28:16 +0300147 if (sset == ETH_SS_TUNABLES)
148 return ARRAY_SIZE(tunable_strings);
149
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +0100150 if (sset == ETH_SS_PHY_TUNABLES)
151 return ARRAY_SIZE(phy_tunable_strings);
152
Florian Fainelli99943382018-04-25 12:12:48 -0700153 if (sset == ETH_SS_PHY_STATS && dev->phydev &&
154 !ops->get_ethtool_phy_stats)
155 return phy_ethtool_get_sset_count(dev->phydev);
Andrew Lunnf3a40942015-12-30 16:28:25 +0100156
Michal Kubecek428c1222019-12-11 10:58:34 +0100157 if (sset == ETH_SS_LINK_MODES)
158 return __ETHTOOL_LINK_MODE_MASK_NBITS;
159
Jiri Pirkoc03a14e2013-01-07 09:02:08 +0000160 if (ops->get_sset_count && ops->get_strings)
Michał Mirosław340ae162011-02-15 16:59:16 +0000161 return ops->get_sset_count(dev, sset);
162 else
163 return -EOPNOTSUPP;
164}
165
166static void __ethtool_get_strings(struct net_device *dev,
167 u32 stringset, u8 *data)
168{
169 const struct ethtool_ops *ops = dev->ethtool_ops;
170
Michał Mirosław5455c692011-02-15 16:59:17 +0000171 if (stringset == ETH_SS_FEATURES)
172 memcpy(data, netdev_features_strings,
173 sizeof(netdev_features_strings));
Eyal Perry892311f2014-12-02 18:12:10 +0200174 else if (stringset == ETH_SS_RSS_HASH_FUNCS)
175 memcpy(data, rss_hash_func_strings,
176 sizeof(rss_hash_func_strings));
Hadar Hen Ziona4244b02015-06-11 10:28:16 +0300177 else if (stringset == ETH_SS_TUNABLES)
178 memcpy(data, tunable_strings, sizeof(tunable_strings));
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +0100179 else if (stringset == ETH_SS_PHY_TUNABLES)
180 memcpy(data, phy_tunable_strings, sizeof(phy_tunable_strings));
Florian Fainelli99943382018-04-25 12:12:48 -0700181 else if (stringset == ETH_SS_PHY_STATS && dev->phydev &&
182 !ops->get_ethtool_phy_stats)
183 phy_ethtool_get_strings(dev->phydev, data);
Michal Kubecek428c1222019-12-11 10:58:34 +0100184 else if (stringset == ETH_SS_LINK_MODES)
185 memcpy(data, link_mode_names,
186 __ETHTOOL_LINK_MODE_MASK_NBITS * ETH_GSTRING_LEN);
Florian Fainelli99943382018-04-25 12:12:48 -0700187 else
Michał Mirosław5455c692011-02-15 16:59:17 +0000188 /* ops->get_strings is valid because checked earlier */
189 ops->get_strings(dev, stringset, data);
Michał Mirosław340ae162011-02-15 16:59:16 +0000190}
191
Michał Mirosławc8f44af2011-11-15 15:29:55 +0000192static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd)
Michał Mirosław0a417702011-02-15 16:59:17 +0000193{
194 /* feature masks of legacy discrete ethtool ops */
195
196 switch (eth_cmd) {
197 case ETHTOOL_GTXCSUM:
198 case ETHTOOL_STXCSUM:
Vladyslav Tarasiuk9d648fb2020-03-24 13:57:08 +0200199 return NETIF_F_CSUM_MASK | NETIF_F_FCOE_CRC |
Michal Kubecekf70bb062020-03-12 21:07:43 +0100200 NETIF_F_SCTP_CRC;
Michał Mirosławe83d3602011-02-15 16:59:18 +0000201 case ETHTOOL_GRXCSUM:
202 case ETHTOOL_SRXCSUM:
203 return NETIF_F_RXCSUM;
Michał Mirosław0a417702011-02-15 16:59:17 +0000204 case ETHTOOL_GSG:
205 case ETHTOOL_SSG:
Michal Kubecekf70bb062020-03-12 21:07:43 +0100206 return NETIF_F_SG | NETIF_F_FRAGLIST;
Michał Mirosław0a417702011-02-15 16:59:17 +0000207 case ETHTOOL_GTSO:
208 case ETHTOOL_STSO:
209 return NETIF_F_ALL_TSO;
Michał Mirosław0a417702011-02-15 16:59:17 +0000210 case ETHTOOL_GGSO:
211 case ETHTOOL_SGSO:
212 return NETIF_F_GSO;
213 case ETHTOOL_GGRO:
214 case ETHTOOL_SGRO:
215 return NETIF_F_GRO;
216 default:
217 BUG();
218 }
219}
220
Michał Mirosław0a417702011-02-15 16:59:17 +0000221static int ethtool_get_one_feature(struct net_device *dev,
222 char __user *useraddr, u32 ethcmd)
223{
Michał Mirosławc8f44af2011-11-15 15:29:55 +0000224 netdev_features_t mask = ethtool_get_feature_mask(ethcmd);
Michał Mirosław0a417702011-02-15 16:59:17 +0000225 struct ethtool_value edata = {
226 .cmd = ethcmd,
Michał Mirosław86794882011-02-15 16:59:17 +0000227 .data = !!(dev->features & mask),
Michał Mirosław0a417702011-02-15 16:59:17 +0000228 };
Michał Mirosław0a417702011-02-15 16:59:17 +0000229
Michał Mirosław0a417702011-02-15 16:59:17 +0000230 if (copy_to_user(useraddr, &edata, sizeof(edata)))
231 return -EFAULT;
232 return 0;
233}
234
Michał Mirosław0a417702011-02-15 16:59:17 +0000235static int ethtool_set_one_feature(struct net_device *dev,
236 void __user *useraddr, u32 ethcmd)
237{
238 struct ethtool_value edata;
Michał Mirosławc8f44af2011-11-15 15:29:55 +0000239 netdev_features_t mask;
Michał Mirosław0a417702011-02-15 16:59:17 +0000240
241 if (copy_from_user(&edata, useraddr, sizeof(edata)))
242 return -EFAULT;
243
Michał Mirosław86794882011-02-15 16:59:17 +0000244 mask = ethtool_get_feature_mask(ethcmd);
245 mask &= dev->hw_features;
Michał Mirosławbc5787c62011-11-15 15:29:55 +0000246 if (!mask)
Michał Mirosław0a417702011-02-15 16:59:17 +0000247 return -EOPNOTSUPP;
Michał Mirosławbc5787c62011-11-15 15:29:55 +0000248
249 if (edata.data)
250 dev->wanted_features |= mask;
251 else
252 dev->wanted_features &= ~mask;
253
254 __netdev_update_features(dev);
255
256 return 0;
Michał Mirosław0a417702011-02-15 16:59:17 +0000257}
258
Michał Mirosław02b3a552011-11-15 15:29:55 +0000259#define ETH_ALL_FLAGS (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | \
260 ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH)
Patrick McHardyf6469682013-04-19 02:04:27 +0000261#define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_CTAG_RX | \
262 NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_NTUPLE | \
263 NETIF_F_RXHASH)
Michał Mirosławbc5787c62011-11-15 15:29:55 +0000264
265static u32 __ethtool_get_flags(struct net_device *dev)
266{
Michał Mirosław02b3a552011-11-15 15:29:55 +0000267 u32 flags = 0;
268
Dragos Foianu8a731252013-07-13 14:43:00 +0100269 if (dev->features & NETIF_F_LRO)
270 flags |= ETH_FLAG_LRO;
271 if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
272 flags |= ETH_FLAG_RXVLAN;
273 if (dev->features & NETIF_F_HW_VLAN_CTAG_TX)
274 flags |= ETH_FLAG_TXVLAN;
275 if (dev->features & NETIF_F_NTUPLE)
276 flags |= ETH_FLAG_NTUPLE;
277 if (dev->features & NETIF_F_RXHASH)
278 flags |= ETH_FLAG_RXHASH;
Michał Mirosław02b3a552011-11-15 15:29:55 +0000279
280 return flags;
Michał Mirosławbc5787c62011-11-15 15:29:55 +0000281}
282
283static int __ethtool_set_flags(struct net_device *dev, u32 data)
Michał Mirosławda8ac86c2011-02-15 16:59:18 +0000284{
Michał Mirosławc8f44af2011-11-15 15:29:55 +0000285 netdev_features_t features = 0, changed;
Michał Mirosławda8ac86c2011-02-15 16:59:18 +0000286
Michał Mirosław02b3a552011-11-15 15:29:55 +0000287 if (data & ~ETH_ALL_FLAGS)
Michał Mirosławda8ac86c2011-02-15 16:59:18 +0000288 return -EINVAL;
289
Dragos Foianu8a731252013-07-13 14:43:00 +0100290 if (data & ETH_FLAG_LRO)
291 features |= NETIF_F_LRO;
292 if (data & ETH_FLAG_RXVLAN)
293 features |= NETIF_F_HW_VLAN_CTAG_RX;
294 if (data & ETH_FLAG_TXVLAN)
295 features |= NETIF_F_HW_VLAN_CTAG_TX;
296 if (data & ETH_FLAG_NTUPLE)
297 features |= NETIF_F_NTUPLE;
298 if (data & ETH_FLAG_RXHASH)
299 features |= NETIF_F_RXHASH;
Michał Mirosław02b3a552011-11-15 15:29:55 +0000300
Michał Mirosławda8ac86c2011-02-15 16:59:18 +0000301 /* allow changing only bits set in hw_features */
Michał Mirosław02b3a552011-11-15 15:29:55 +0000302 changed = (features ^ dev->features) & ETH_ALL_FEATURES;
Michał Mirosławda8ac86c2011-02-15 16:59:18 +0000303 if (changed & ~dev->hw_features)
304 return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP;
305
306 dev->wanted_features =
Michał Mirosław02b3a552011-11-15 15:29:55 +0000307 (dev->wanted_features & ~changed) | (features & changed);
Michał Mirosławda8ac86c2011-02-15 16:59:18 +0000308
Michał Mirosław6cb6a272011-04-02 22:48:47 -0700309 __netdev_update_features(dev);
Michał Mirosławda8ac86c2011-02-15 16:59:18 +0000310
311 return 0;
312}
313
Alan Brady5a6cd6d2017-10-05 14:53:40 -0700314/* Given two link masks, AND them together and save the result in dst. */
315void ethtool_intersect_link_masks(struct ethtool_link_ksettings *dst,
316 struct ethtool_link_ksettings *src)
317{
318 unsigned int size = BITS_TO_LONGS(__ETHTOOL_LINK_MODE_MASK_NBITS);
319 unsigned int idx = 0;
320
321 for (; idx < size; idx++) {
322 dst->link_modes.supported[idx] &=
323 src->link_modes.supported[idx];
324 dst->link_modes.advertising[idx] &=
325 src->link_modes.advertising[idx];
326 }
327}
328EXPORT_SYMBOL(ethtool_intersect_link_masks);
329
Philippe Reynes6d62b4d2016-04-15 00:34:59 +0200330void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst,
331 u32 legacy_u32)
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800332{
333 bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS);
334 dst[0] = legacy_u32;
335}
Philippe Reynes6d62b4d2016-04-15 00:34:59 +0200336EXPORT_SYMBOL(ethtool_convert_legacy_u32_to_link_mode);
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800337
338/* return false if src had higher bits set. lower bits always updated. */
Philippe Reynes6d62b4d2016-04-15 00:34:59 +0200339bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
340 const unsigned long *src)
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800341{
342 bool retval = true;
343
344 /* TODO: following test will soon always be true */
345 if (__ETHTOOL_LINK_MODE_MASK_NBITS > 32) {
346 __ETHTOOL_DECLARE_LINK_MODE_MASK(ext);
347
348 bitmap_zero(ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
349 bitmap_fill(ext, 32);
350 bitmap_complement(ext, ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
351 if (bitmap_intersects(ext, src,
352 __ETHTOOL_LINK_MODE_MASK_NBITS)) {
353 /* src mask goes beyond bit 31 */
354 retval = false;
355 }
356 }
357 *legacy_u32 = src[0];
358 return retval;
359}
Philippe Reynes6d62b4d2016-04-15 00:34:59 +0200360EXPORT_SYMBOL(ethtool_convert_link_mode_to_legacy_u32);
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800361
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800362/* return false if ksettings link modes had higher bits
363 * set. legacy_settings always updated (best effort)
364 */
365static bool
366convert_link_ksettings_to_legacy_settings(
367 struct ethtool_cmd *legacy_settings,
368 const struct ethtool_link_ksettings *link_ksettings)
369{
370 bool retval = true;
371
372 memset(legacy_settings, 0, sizeof(*legacy_settings));
373 /* this also clears the deprecated fields in legacy structure:
374 * __u8 transceiver;
375 * __u32 maxtxpkt;
376 * __u32 maxrxpkt;
377 */
378
Philippe Reynes6d62b4d2016-04-15 00:34:59 +0200379 retval &= ethtool_convert_link_mode_to_legacy_u32(
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800380 &legacy_settings->supported,
381 link_ksettings->link_modes.supported);
Philippe Reynes6d62b4d2016-04-15 00:34:59 +0200382 retval &= ethtool_convert_link_mode_to_legacy_u32(
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800383 &legacy_settings->advertising,
384 link_ksettings->link_modes.advertising);
Philippe Reynes6d62b4d2016-04-15 00:34:59 +0200385 retval &= ethtool_convert_link_mode_to_legacy_u32(
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800386 &legacy_settings->lp_advertising,
387 link_ksettings->link_modes.lp_advertising);
388 ethtool_cmd_speed_set(legacy_settings, link_ksettings->base.speed);
389 legacy_settings->duplex
390 = link_ksettings->base.duplex;
391 legacy_settings->port
392 = link_ksettings->base.port;
393 legacy_settings->phy_address
394 = link_ksettings->base.phy_address;
395 legacy_settings->autoneg
396 = link_ksettings->base.autoneg;
397 legacy_settings->mdio_support
398 = link_ksettings->base.mdio_support;
399 legacy_settings->eth_tp_mdix
400 = link_ksettings->base.eth_tp_mdix;
401 legacy_settings->eth_tp_mdix_ctrl
402 = link_ksettings->base.eth_tp_mdix_ctrl;
Florian Fainelli19cab882017-09-20 15:52:13 -0700403 legacy_settings->transceiver
404 = link_ksettings->base.transceiver;
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800405 return retval;
406}
407
408/* number of 32-bit words to store the user's link mode bitmaps */
409#define __ETHTOOL_LINK_MODE_MASK_NU32 \
410 DIV_ROUND_UP(__ETHTOOL_LINK_MODE_MASK_NBITS, 32)
411
412/* layout of the struct passed from/to userland */
413struct ethtool_link_usettings {
414 struct ethtool_link_settings base;
415 struct {
416 __u32 supported[__ETHTOOL_LINK_MODE_MASK_NU32];
417 __u32 advertising[__ETHTOOL_LINK_MODE_MASK_NU32];
418 __u32 lp_advertising[__ETHTOOL_LINK_MODE_MASK_NU32];
419 } link_modes;
420};
421
Michal Kubecek9b300492018-08-28 19:56:58 +0200422/* Internal kernel helper to query a device ethtool_link_settings. */
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800423int __ethtool_get_link_ksettings(struct net_device *dev,
424 struct ethtool_link_ksettings *link_ksettings)
425{
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800426 ASSERT_RTNL();
427
Michal Kubecek9b300492018-08-28 19:56:58 +0200428 if (!dev->ethtool_ops->get_link_ksettings)
David Decotigny3237fc62016-02-24 10:58:11 -0800429 return -EOPNOTSUPP;
430
Michal Kubecek9b300492018-08-28 19:56:58 +0200431 memset(link_ksettings, 0, sizeof(*link_ksettings));
432 return dev->ethtool_ops->get_link_ksettings(dev, link_ksettings);
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800433}
434EXPORT_SYMBOL(__ethtool_get_link_ksettings);
435
436/* convert ethtool_link_usettings in user space to a kernel internal
437 * ethtool_link_ksettings. return 0 on success, errno on error.
438 */
439static int load_link_ksettings_from_user(struct ethtool_link_ksettings *to,
440 const void __user *from)
441{
442 struct ethtool_link_usettings link_usettings;
443
444 if (copy_from_user(&link_usettings, from, sizeof(link_usettings)))
445 return -EFAULT;
446
447 memcpy(&to->base, &link_usettings.base, sizeof(to->base));
Yury Norov3aa56882018-02-06 15:38:06 -0800448 bitmap_from_arr32(to->link_modes.supported,
449 link_usettings.link_modes.supported,
450 __ETHTOOL_LINK_MODE_MASK_NBITS);
451 bitmap_from_arr32(to->link_modes.advertising,
452 link_usettings.link_modes.advertising,
453 __ETHTOOL_LINK_MODE_MASK_NBITS);
454 bitmap_from_arr32(to->link_modes.lp_advertising,
455 link_usettings.link_modes.lp_advertising,
456 __ETHTOOL_LINK_MODE_MASK_NBITS);
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800457
458 return 0;
459}
460
Cris Forno70ae1e12020-02-28 14:12:04 -0600461/* Check if the user is trying to change anything besides speed/duplex */
462bool ethtool_virtdev_validate_cmd(const struct ethtool_link_ksettings *cmd)
463{
464 struct ethtool_link_settings base2 = {};
465
466 base2.speed = cmd->base.speed;
467 base2.port = PORT_OTHER;
468 base2.duplex = cmd->base.duplex;
469 base2.cmd = cmd->base.cmd;
470 base2.link_mode_masks_nwords = cmd->base.link_mode_masks_nwords;
471
472 return !memcmp(&base2, &cmd->base, sizeof(base2)) &&
473 bitmap_empty(cmd->link_modes.supported,
474 __ETHTOOL_LINK_MODE_MASK_NBITS) &&
475 bitmap_empty(cmd->link_modes.lp_advertising,
476 __ETHTOOL_LINK_MODE_MASK_NBITS);
477}
478
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800479/* convert a kernel internal ethtool_link_ksettings to
480 * ethtool_link_usettings in user space. return 0 on success, errno on
481 * error.
482 */
483static int
484store_link_ksettings_for_user(void __user *to,
485 const struct ethtool_link_ksettings *from)
486{
487 struct ethtool_link_usettings link_usettings;
488
489 memcpy(&link_usettings.base, &from->base, sizeof(link_usettings));
Yury Norov3aa56882018-02-06 15:38:06 -0800490 bitmap_to_arr32(link_usettings.link_modes.supported,
491 from->link_modes.supported,
492 __ETHTOOL_LINK_MODE_MASK_NBITS);
493 bitmap_to_arr32(link_usettings.link_modes.advertising,
494 from->link_modes.advertising,
495 __ETHTOOL_LINK_MODE_MASK_NBITS);
496 bitmap_to_arr32(link_usettings.link_modes.lp_advertising,
497 from->link_modes.lp_advertising,
498 __ETHTOOL_LINK_MODE_MASK_NBITS);
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800499
500 if (copy_to_user(to, &link_usettings, sizeof(link_usettings)))
501 return -EFAULT;
502
503 return 0;
504}
505
Michal Kubecek9b300492018-08-28 19:56:58 +0200506/* Query device for its ethtool_link_settings. */
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800507static int ethtool_get_link_ksettings(struct net_device *dev,
508 void __user *useraddr)
509{
510 int err = 0;
511 struct ethtool_link_ksettings link_ksettings;
512
513 ASSERT_RTNL();
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800514 if (!dev->ethtool_ops->get_link_ksettings)
515 return -EOPNOTSUPP;
516
517 /* handle bitmap nbits handshake */
518 if (copy_from_user(&link_ksettings.base, useraddr,
519 sizeof(link_ksettings.base)))
520 return -EFAULT;
521
522 if (__ETHTOOL_LINK_MODE_MASK_NU32
523 != link_ksettings.base.link_mode_masks_nwords) {
524 /* wrong link mode nbits requested */
525 memset(&link_ksettings, 0, sizeof(link_ksettings));
Ben Hutchings793cf872016-03-14 01:05:38 +0000526 link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800527 /* send back number of words required as negative val */
528 compiletime_assert(__ETHTOOL_LINK_MODE_MASK_NU32 <= S8_MAX,
529 "need too many bits for link modes!");
530 link_ksettings.base.link_mode_masks_nwords
531 = -((s8)__ETHTOOL_LINK_MODE_MASK_NU32);
532
533 /* copy the base fields back to user, not the link
534 * mode bitmaps
535 */
536 if (copy_to_user(useraddr, &link_ksettings.base,
537 sizeof(link_ksettings.base)))
538 return -EFAULT;
539
540 return 0;
541 }
542
543 /* handshake successful: user/kernel agree on
544 * link_mode_masks_nwords
545 */
546
547 memset(&link_ksettings, 0, sizeof(link_ksettings));
548 err = dev->ethtool_ops->get_link_ksettings(dev, &link_ksettings);
549 if (err < 0)
550 return err;
551
552 /* make sure we tell the right values to user */
553 link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
554 link_ksettings.base.link_mode_masks_nwords
555 = __ETHTOOL_LINK_MODE_MASK_NU32;
556
557 return store_link_ksettings_for_user(useraddr, &link_ksettings);
558}
559
Michal Kubecek9b300492018-08-28 19:56:58 +0200560/* Update device ethtool_link_settings. */
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800561static int ethtool_set_link_ksettings(struct net_device *dev,
562 void __user *useraddr)
563{
564 int err;
565 struct ethtool_link_ksettings link_ksettings;
566
567 ASSERT_RTNL();
568
569 if (!dev->ethtool_ops->set_link_ksettings)
570 return -EOPNOTSUPP;
571
572 /* make sure nbits field has expected value */
573 if (copy_from_user(&link_ksettings.base, useraddr,
574 sizeof(link_ksettings.base)))
575 return -EFAULT;
576
577 if (__ETHTOOL_LINK_MODE_MASK_NU32
578 != link_ksettings.base.link_mode_masks_nwords)
579 return -EINVAL;
580
581 /* copy the whole structure, now that we know it has expected
582 * format
583 */
584 err = load_link_ksettings_from_user(&link_ksettings, useraddr);
585 if (err)
586 return err;
587
588 /* re-check nwords field, just in case */
589 if (__ETHTOOL_LINK_MODE_MASK_NU32
590 != link_ksettings.base.link_mode_masks_nwords)
591 return -EINVAL;
592
Michal Kubecek73286732019-12-27 15:56:03 +0100593 err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
Michal Kubecek1b1b1842019-12-27 15:56:18 +0100594 if (err >= 0) {
Michal Kubecek73286732019-12-27 15:56:03 +0100595 ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
Michal Kubecek1b1b1842019-12-27 15:56:18 +0100596 ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL);
597 }
Michal Kubecek73286732019-12-27 15:56:03 +0100598 return err;
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800599}
600
Cris Forno70ae1e12020-02-28 14:12:04 -0600601int ethtool_virtdev_set_link_ksettings(struct net_device *dev,
602 const struct ethtool_link_ksettings *cmd,
603 u32 *dev_speed, u8 *dev_duplex)
604{
605 u32 speed;
606 u8 duplex;
607
608 speed = cmd->base.speed;
609 duplex = cmd->base.duplex;
610 /* don't allow custom speed and duplex */
611 if (!ethtool_validate_speed(speed) ||
612 !ethtool_validate_duplex(duplex) ||
613 !ethtool_virtdev_validate_cmd(cmd))
614 return -EINVAL;
615 *dev_speed = speed;
616 *dev_duplex = duplex;
617
618 return 0;
619}
620EXPORT_SYMBOL(ethtool_virtdev_set_link_ksettings);
621
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800622/* Query device for its ethtool_cmd settings.
623 *
Michal Kubecek9b300492018-08-28 19:56:58 +0200624 * Backward compatibility note: for compatibility with legacy ethtool, this is
625 * now implemented via get_link_ksettings. When driver reports higher link mode
626 * bits, a kernel warning is logged once (with name of 1st driver/device) to
627 * recommend user to upgrade ethtool, but the command is successful (only the
628 * lower link mode bits reported back to user). Deprecated fields from
629 * ethtool_cmd (transceiver/maxrxpkt/maxtxpkt) are always set to zero.
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800630 */
Jiri Pirko4bc71cb2011-09-03 03:34:30 +0000631static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
632{
Michal Kubecek9b300492018-08-28 19:56:58 +0200633 struct ethtool_link_ksettings link_ksettings;
Jiri Pirko4bc71cb2011-09-03 03:34:30 +0000634 struct ethtool_cmd cmd;
Michal Kubecek9b300492018-08-28 19:56:58 +0200635 int err;
Jiri Pirko4bc71cb2011-09-03 03:34:30 +0000636
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800637 ASSERT_RTNL();
Michal Kubecek9b300492018-08-28 19:56:58 +0200638 if (!dev->ethtool_ops->get_link_ksettings)
639 return -EOPNOTSUPP;
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800640
Michal Kubecek9b300492018-08-28 19:56:58 +0200641 memset(&link_ksettings, 0, sizeof(link_ksettings));
642 err = dev->ethtool_ops->get_link_ksettings(dev, &link_ksettings);
643 if (err < 0)
644 return err;
645 convert_link_ksettings_to_legacy_settings(&cmd, &link_ksettings);
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800646
Michal Kubecek9b300492018-08-28 19:56:58 +0200647 /* send a sensible cmd tag back to user */
648 cmd.cmd = ETHTOOL_GSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
650 if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
651 return -EFAULT;
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 return 0;
654}
655
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800656/* Update device link settings with given ethtool_cmd.
657 *
Michal Kubecek9b300492018-08-28 19:56:58 +0200658 * Backward compatibility note: for compatibility with legacy ethtool, this is
659 * now always implemented via set_link_settings. When user's request updates
660 * deprecated ethtool_cmd fields (transceiver/maxrxpkt/maxtxpkt), a kernel
661 * warning is logged once (with name of 1st driver/device) to recommend user to
662 * upgrade ethtool, and the request is rejected.
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800663 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
665{
Michal Kubecek9b300492018-08-28 19:56:58 +0200666 struct ethtool_link_ksettings link_ksettings;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 struct ethtool_cmd cmd;
Michal Kubecek73286732019-12-27 15:56:03 +0100668 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800670 ASSERT_RTNL();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
672 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
673 return -EFAULT;
Michal Kubecek9b300492018-08-28 19:56:58 +0200674 if (!dev->ethtool_ops->set_link_ksettings)
David Decotigny3f1ac7a2016-02-24 10:57:59 -0800675 return -EOPNOTSUPP;
676
Michal Kubecek9b300492018-08-28 19:56:58 +0200677 if (!convert_legacy_settings_to_link_ksettings(&link_ksettings, &cmd))
678 return -EINVAL;
679 link_ksettings.base.link_mode_masks_nwords =
680 __ETHTOOL_LINK_MODE_MASK_NU32;
Michal Kubecek73286732019-12-27 15:56:03 +0100681 ret = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
Michal Kubecek1b1b1842019-12-27 15:56:18 +0100682 if (ret >= 0) {
Michal Kubecek73286732019-12-27 15:56:03 +0100683 ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
Michal Kubecek1b1b1842019-12-27 15:56:18 +0100684 ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL);
685 }
Michal Kubecek73286732019-12-27 15:56:03 +0100686 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687}
688
chavey97f8aef2010-04-07 21:54:42 -0700689static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
690 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
692 struct ethtool_drvinfo info;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700693 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 memset(&info, 0, sizeof(info));
696 info.cmd = ETHTOOL_GDRVINFO;
Leon Romanovsky6a7e25c2020-01-27 09:20:28 +0200697 strlcpy(info.version, UTS_RELEASE, sizeof(info.version));
Jiri Pirkoc03a14e2013-01-07 09:02:08 +0000698 if (ops->get_drvinfo) {
Ben Hutchings01414802010-08-17 02:31:15 -0700699 ops->get_drvinfo(dev, &info);
700 } else if (dev->dev.parent && dev->dev.parent->driver) {
701 strlcpy(info.bus_info, dev_name(dev->dev.parent),
702 sizeof(info.bus_info));
703 strlcpy(info.driver, dev->dev.parent->driver->name,
704 sizeof(info.driver));
705 } else {
706 return -EOPNOTSUPP;
707 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
Jeff Garzik723b2f52010-03-03 22:51:50 +0000709 /*
710 * this method of obtaining string set info is deprecated;
Jeff Garzikd17792e2010-03-04 08:21:53 +0000711 * Use ETHTOOL_GSSET_INFO instead.
Jeff Garzik723b2f52010-03-03 22:51:50 +0000712 */
Jiri Pirkoc03a14e2013-01-07 09:02:08 +0000713 if (ops->get_sset_count) {
Jeff Garzikff03d492007-08-15 16:01:08 -0700714 int rc;
715
716 rc = ops->get_sset_count(dev, ETH_SS_TEST);
717 if (rc >= 0)
718 info.testinfo_len = rc;
719 rc = ops->get_sset_count(dev, ETH_SS_STATS);
720 if (rc >= 0)
721 info.n_stats = rc;
Jeff Garzik339bf022007-08-15 16:01:32 -0700722 rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
723 if (rc >= 0)
724 info.n_priv_flags = rc;
Jeff Garzikff03d492007-08-15 16:01:08 -0700725 }
Yunsheng Linf9fc54d2018-12-26 19:51:46 +0800726 if (ops->get_regs_len) {
727 int ret = ops->get_regs_len(dev);
728
729 if (ret > 0)
730 info.regdump_len = ret;
731 }
732
Jiri Pirkoc03a14e2013-01-07 09:02:08 +0000733 if (ops->get_eeprom_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 info.eedump_len = ops->get_eeprom_len(dev);
735
Jakub Kicinskiddb6e992019-01-31 10:50:47 -0800736 if (!info.fw_version[0])
737 devlink_compat_running_version(dev, info.fw_version,
738 sizeof(info.fw_version));
Jakub Kicinskiddb6e992019-01-31 10:50:47 -0800739
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 if (copy_to_user(useraddr, &info, sizeof(info)))
741 return -EFAULT;
742 return 0;
743}
744
Eric Dumazetf5c445e2010-03-08 12:17:04 -0800745static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
chavey97f8aef2010-04-07 21:54:42 -0700746 void __user *useraddr)
Jeff Garzik723b2f52010-03-03 22:51:50 +0000747{
748 struct ethtool_sset_info info;
Jeff Garzik723b2f52010-03-03 22:51:50 +0000749 u64 sset_mask;
750 int i, idx = 0, n_bits = 0, ret, rc;
751 u32 *info_buf = NULL;
752
Jeff Garzik723b2f52010-03-03 22:51:50 +0000753 if (copy_from_user(&info, useraddr, sizeof(info)))
754 return -EFAULT;
755
756 /* store copy of mask, because we zero struct later on */
757 sset_mask = info.sset_mask;
758 if (!sset_mask)
759 return 0;
760
761 /* calculate size of return buffer */
Jeff Garzikd17792e2010-03-04 08:21:53 +0000762 n_bits = hweight64(sset_mask);
Jeff Garzik723b2f52010-03-03 22:51:50 +0000763
764 memset(&info, 0, sizeof(info));
765 info.cmd = ETHTOOL_GSSET_INFO;
766
Kees Cook6396bb22018-06-12 14:03:40 -0700767 info_buf = kcalloc(n_bits, sizeof(u32), GFP_USER);
Jeff Garzik723b2f52010-03-03 22:51:50 +0000768 if (!info_buf)
769 return -ENOMEM;
770
771 /*
772 * fill return buffer based on input bitmask and successful
773 * get_sset_count return
774 */
775 for (i = 0; i < 64; i++) {
776 if (!(sset_mask & (1ULL << i)))
777 continue;
778
Michał Mirosław340ae162011-02-15 16:59:16 +0000779 rc = __ethtool_get_sset_count(dev, i);
Jeff Garzik723b2f52010-03-03 22:51:50 +0000780 if (rc >= 0) {
781 info.sset_mask |= (1ULL << i);
782 info_buf[idx++] = rc;
783 }
784 }
785
786 ret = -EFAULT;
787 if (copy_to_user(useraddr, &info, sizeof(info)))
788 goto out;
789
790 useraddr += offsetof(struct ethtool_sset_info, data);
791 if (copy_to_user(useraddr, info_buf, idx * sizeof(u32)))
792 goto out;
793
794 ret = 0;
795
796out:
797 kfree(info_buf);
798 return ret;
799}
800
chavey97f8aef2010-04-07 21:54:42 -0700801static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
Ben Hutchingsbf988432010-06-28 08:45:58 +0000802 u32 cmd, void __user *useraddr)
Santwona Behera0853ad62008-07-02 03:47:41 -0700803{
Ben Hutchingsbf988432010-06-28 08:45:58 +0000804 struct ethtool_rxnfc info;
805 size_t info_size = sizeof(info);
Ben Hutchings55664f32012-01-03 12:04:51 +0000806 int rc;
Santwona Behera0853ad62008-07-02 03:47:41 -0700807
Santwona Behera59089d82009-02-20 00:58:13 -0800808 if (!dev->ethtool_ops->set_rxnfc)
Santwona Behera0853ad62008-07-02 03:47:41 -0700809 return -EOPNOTSUPP;
810
Ben Hutchingsbf988432010-06-28 08:45:58 +0000811 /* struct ethtool_rxnfc was originally defined for
812 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
813 * members. User-space might still be using that
814 * definition. */
815 if (cmd == ETHTOOL_SRXFH)
816 info_size = (offsetof(struct ethtool_rxnfc, data) +
817 sizeof(info.data));
818
819 if (copy_from_user(&info, useraddr, info_size))
Santwona Behera0853ad62008-07-02 03:47:41 -0700820 return -EFAULT;
821
Ben Hutchings55664f32012-01-03 12:04:51 +0000822 rc = dev->ethtool_ops->set_rxnfc(dev, &info);
823 if (rc)
824 return rc;
825
826 if (cmd == ETHTOOL_SRXCLSRLINS &&
827 copy_to_user(useraddr, &info, info_size))
828 return -EFAULT;
829
830 return 0;
Santwona Behera0853ad62008-07-02 03:47:41 -0700831}
832
chavey97f8aef2010-04-07 21:54:42 -0700833static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
Ben Hutchingsbf988432010-06-28 08:45:58 +0000834 u32 cmd, void __user *useraddr)
Santwona Behera0853ad62008-07-02 03:47:41 -0700835{
836 struct ethtool_rxnfc info;
Ben Hutchingsbf988432010-06-28 08:45:58 +0000837 size_t info_size = sizeof(info);
Santwona Behera59089d82009-02-20 00:58:13 -0800838 const struct ethtool_ops *ops = dev->ethtool_ops;
839 int ret;
840 void *rule_buf = NULL;
Santwona Behera0853ad62008-07-02 03:47:41 -0700841
Santwona Behera59089d82009-02-20 00:58:13 -0800842 if (!ops->get_rxnfc)
Santwona Behera0853ad62008-07-02 03:47:41 -0700843 return -EOPNOTSUPP;
844
Ben Hutchingsbf988432010-06-28 08:45:58 +0000845 /* struct ethtool_rxnfc was originally defined for
846 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
847 * members. User-space might still be using that
848 * definition. */
849 if (cmd == ETHTOOL_GRXFH)
850 info_size = (offsetof(struct ethtool_rxnfc, data) +
851 sizeof(info.data));
852
853 if (copy_from_user(&info, useraddr, info_size))
Santwona Behera0853ad62008-07-02 03:47:41 -0700854 return -EFAULT;
855
Edward Cree84a1d9c42018-03-08 15:45:03 +0000856 /* If FLOW_RSS was requested then user-space must be using the
857 * new definition, as FLOW_RSS is newer.
858 */
859 if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) {
860 info_size = sizeof(info);
861 if (copy_from_user(&info, useraddr, info_size))
862 return -EFAULT;
Wenwen Wangd656fe42018-04-30 12:31:13 -0500863 /* Since malicious users may modify the original data,
864 * we need to check whether FLOW_RSS is still requested.
865 */
866 if (!(info.flow_type & FLOW_RSS))
867 return -EINVAL;
Edward Cree84a1d9c42018-03-08 15:45:03 +0000868 }
869
Wenwen Wang2bb32072018-10-09 08:15:38 -0500870 if (info.cmd != cmd)
871 return -EINVAL;
872
Santwona Behera59089d82009-02-20 00:58:13 -0800873 if (info.cmd == ETHTOOL_GRXCLSRLALL) {
874 if (info.rule_cnt > 0) {
Ben Hutchingsdb048b62010-06-28 08:44:07 +0000875 if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
Kees Cook6396bb22018-06-12 14:03:40 -0700876 rule_buf = kcalloc(info.rule_cnt, sizeof(u32),
Ben Hutchingsdb048b62010-06-28 08:44:07 +0000877 GFP_USER);
Santwona Behera59089d82009-02-20 00:58:13 -0800878 if (!rule_buf)
879 return -ENOMEM;
880 }
881 }
Santwona Behera0853ad62008-07-02 03:47:41 -0700882
Santwona Behera59089d82009-02-20 00:58:13 -0800883 ret = ops->get_rxnfc(dev, &info, rule_buf);
884 if (ret < 0)
885 goto err_out;
886
887 ret = -EFAULT;
Ben Hutchingsbf988432010-06-28 08:45:58 +0000888 if (copy_to_user(useraddr, &info, info_size))
Santwona Behera59089d82009-02-20 00:58:13 -0800889 goto err_out;
890
891 if (rule_buf) {
892 useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
893 if (copy_to_user(useraddr, rule_buf,
894 info.rule_cnt * sizeof(u32)))
895 goto err_out;
896 }
897 ret = 0;
898
899err_out:
Wei Yongjunc9cacec2009-03-31 15:06:26 -0700900 kfree(rule_buf);
Santwona Behera59089d82009-02-20 00:58:13 -0800901
902 return ret;
Santwona Behera0853ad62008-07-02 03:47:41 -0700903}
904
Venkata Duvvuru3de0b592014-04-21 15:37:59 +0530905static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
906 struct ethtool_rxnfc *rx_rings,
907 u32 size)
908{
Ben Hutchingsfb95cd82014-05-15 00:46:45 +0100909 int i;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +0530910
911 if (copy_from_user(indir, useraddr, size * sizeof(indir[0])))
Ben Hutchingsfb95cd82014-05-15 00:46:45 +0100912 return -EFAULT;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +0530913
914 /* Validate ring indices */
Ben Hutchingsfb95cd82014-05-15 00:46:45 +0100915 for (i = 0; i < size; i++)
916 if (indir[i] >= rx_rings->data)
917 return -EINVAL;
918
919 return 0;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +0530920}
921
Kim Jonesba905f52016-02-02 03:51:16 +0000922u8 netdev_rss_key[NETDEV_RSS_KEY_LEN] __read_mostly;
Eric Dumazet960fb622014-11-16 06:23:05 -0800923
924void netdev_rss_key_fill(void *buffer, size_t len)
925{
926 BUG_ON(len > sizeof(netdev_rss_key));
927 net_get_random_once(netdev_rss_key, sizeof(netdev_rss_key));
928 memcpy(buffer, netdev_rss_key, len);
929}
930EXPORT_SYMBOL(netdev_rss_key_fill);
931
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000932static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
933 void __user *useraddr)
934{
Ben Hutchings7850f632011-12-15 13:55:01 +0000935 u32 user_size, dev_size;
936 u32 *indir;
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000937 int ret;
938
Ben Hutchings7850f632011-12-15 13:55:01 +0000939 if (!dev->ethtool_ops->get_rxfh_indir_size ||
Ben Hutchingsfe62d002014-05-15 01:25:27 +0100940 !dev->ethtool_ops->get_rxfh)
Ben Hutchings7850f632011-12-15 13:55:01 +0000941 return -EOPNOTSUPP;
942 dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
943 if (dev_size == 0)
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000944 return -EOPNOTSUPP;
945
Ben Hutchings7850f632011-12-15 13:55:01 +0000946 if (copy_from_user(&user_size,
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000947 useraddr + offsetof(struct ethtool_rxfh_indir, size),
Ben Hutchings7850f632011-12-15 13:55:01 +0000948 sizeof(user_size)))
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000949 return -EFAULT;
950
Ben Hutchings7850f632011-12-15 13:55:01 +0000951 if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size),
952 &dev_size, sizeof(dev_size)))
953 return -EFAULT;
954
955 /* If the user buffer size is 0, this is just a query for the
956 * device table size. Otherwise, if it's smaller than the
957 * device table size it's an error.
958 */
959 if (user_size < dev_size)
960 return user_size == 0 ? 0 : -EINVAL;
961
962 indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000963 if (!indir)
964 return -ENOMEM;
965
Eyal Perry892311f2014-12-02 18:12:10 +0200966 ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000967 if (ret)
968 goto out;
969
Ben Hutchings7850f632011-12-15 13:55:01 +0000970 if (copy_to_user(useraddr +
971 offsetof(struct ethtool_rxfh_indir, ring_index[0]),
972 indir, dev_size * sizeof(indir[0])))
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000973 ret = -EFAULT;
974
975out:
976 kfree(indir);
977 return ret;
978}
979
980static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
981 void __user *useraddr)
982{
Ben Hutchings7850f632011-12-15 13:55:01 +0000983 struct ethtool_rxnfc rx_rings;
984 u32 user_size, dev_size, i;
985 u32 *indir;
Jiri Pirkoc03a14e2013-01-07 09:02:08 +0000986 const struct ethtool_ops *ops = dev->ethtool_ops;
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000987 int ret;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +0530988 u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]);
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000989
Ben Hutchingsfe62d002014-05-15 01:25:27 +0100990 if (!ops->get_rxfh_indir_size || !ops->set_rxfh ||
Jiri Pirkoc03a14e2013-01-07 09:02:08 +0000991 !ops->get_rxnfc)
Ben Hutchings7850f632011-12-15 13:55:01 +0000992 return -EOPNOTSUPP;
Jiri Pirkoc03a14e2013-01-07 09:02:08 +0000993
994 dev_size = ops->get_rxfh_indir_size(dev);
Ben Hutchings7850f632011-12-15 13:55:01 +0000995 if (dev_size == 0)
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000996 return -EOPNOTSUPP;
997
Ben Hutchings7850f632011-12-15 13:55:01 +0000998 if (copy_from_user(&user_size,
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000999 useraddr + offsetof(struct ethtool_rxfh_indir, size),
Ben Hutchings7850f632011-12-15 13:55:01 +00001000 sizeof(user_size)))
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +00001001 return -EFAULT;
1002
Ben Hutchings278bc422011-12-15 13:56:49 +00001003 if (user_size != 0 && user_size != dev_size)
Ben Hutchings7850f632011-12-15 13:55:01 +00001004 return -EINVAL;
1005
1006 indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +00001007 if (!indir)
1008 return -ENOMEM;
1009
Ben Hutchings7850f632011-12-15 13:55:01 +00001010 rx_rings.cmd = ETHTOOL_GRXRINGS;
Jiri Pirkoc03a14e2013-01-07 09:02:08 +00001011 ret = ops->get_rxnfc(dev, &rx_rings, NULL);
Ben Hutchings7850f632011-12-15 13:55:01 +00001012 if (ret)
1013 goto out;
Ben Hutchings278bc422011-12-15 13:56:49 +00001014
1015 if (user_size == 0) {
1016 for (i = 0; i < dev_size; i++)
1017 indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
1018 } else {
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301019 ret = ethtool_copy_validate_indir(indir,
1020 useraddr + ringidx_offset,
1021 &rx_rings,
1022 dev_size);
1023 if (ret)
Ben Hutchings7850f632011-12-15 13:55:01 +00001024 goto out;
Ben Hutchings7850f632011-12-15 13:55:01 +00001025 }
1026
Eyal Perry892311f2014-12-02 18:12:10 +02001027 ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
Keller, Jacob Ed4ab4282016-02-08 16:05:03 -08001028 if (ret)
1029 goto out;
1030
1031 /* indicate whether rxfh was set to default */
1032 if (user_size == 0)
1033 dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
1034 else
1035 dev->priv_flags |= IFF_RXFH_CONFIGURED;
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +00001036
1037out:
1038 kfree(indir);
1039 return ret;
1040}
1041
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301042static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
1043 void __user *useraddr)
1044{
1045 int ret;
1046 const struct ethtool_ops *ops = dev->ethtool_ops;
Ben Hutchingsf062a382014-05-15 16:28:07 +01001047 u32 user_indir_size, user_key_size;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301048 u32 dev_indir_size = 0, dev_key_size = 0;
Ben Hutchingsf062a382014-05-15 16:28:07 +01001049 struct ethtool_rxfh rxfh;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301050 u32 total_size;
Ben Hutchingsf062a382014-05-15 16:28:07 +01001051 u32 indir_bytes;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301052 u32 *indir = NULL;
Eyal Perry892311f2014-12-02 18:12:10 +02001053 u8 dev_hfunc = 0;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301054 u8 *hkey = NULL;
1055 u8 *rss_config;
1056
Eyal Perry892311f2014-12-02 18:12:10 +02001057 if (!ops->get_rxfh)
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301058 return -EOPNOTSUPP;
1059
1060 if (ops->get_rxfh_indir_size)
1061 dev_indir_size = ops->get_rxfh_indir_size(dev);
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301062 if (ops->get_rxfh_key_size)
1063 dev_key_size = ops->get_rxfh_key_size(dev);
1064
Ben Hutchingsf062a382014-05-15 16:28:07 +01001065 if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301066 return -EFAULT;
Ben Hutchingsf062a382014-05-15 16:28:07 +01001067 user_indir_size = rxfh.indir_size;
1068 user_key_size = rxfh.key_size;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301069
Ben Hutchingsf062a382014-05-15 16:28:07 +01001070 /* Check that reserved fields are 0 for now */
Edward Cree84a1d9c42018-03-08 15:45:03 +00001071 if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32)
Ben Hutchingsf062a382014-05-15 16:28:07 +01001072 return -EINVAL;
Edward Cree84a1d9c42018-03-08 15:45:03 +00001073 /* Most drivers don't handle rss_context, check it's 0 as well */
1074 if (rxfh.rss_context && !ops->get_rxfh_context)
1075 return -EOPNOTSUPP;
Ben Hutchingsf062a382014-05-15 16:28:07 +01001076
1077 rxfh.indir_size = dev_indir_size;
1078 rxfh.key_size = dev_key_size;
1079 if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301080 return -EFAULT;
1081
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301082 if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
1083 (user_key_size && (user_key_size != dev_key_size)))
1084 return -EINVAL;
1085
1086 indir_bytes = user_indir_size * sizeof(indir[0]);
1087 total_size = indir_bytes + user_key_size;
1088 rss_config = kzalloc(total_size, GFP_USER);
1089 if (!rss_config)
1090 return -ENOMEM;
1091
1092 if (user_indir_size)
1093 indir = (u32 *)rss_config;
1094
1095 if (user_key_size)
1096 hkey = rss_config + indir_bytes;
1097
Edward Cree84a1d9c42018-03-08 15:45:03 +00001098 if (rxfh.rss_context)
1099 ret = dev->ethtool_ops->get_rxfh_context(dev, indir, hkey,
1100 &dev_hfunc,
1101 rxfh.rss_context);
1102 else
1103 ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc);
Eyal Perry892311f2014-12-02 18:12:10 +02001104 if (ret)
1105 goto out;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301106
Eyal Perry892311f2014-12-02 18:12:10 +02001107 if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, hfunc),
1108 &dev_hfunc, sizeof(rxfh.hfunc))) {
1109 ret = -EFAULT;
1110 } else if (copy_to_user(useraddr +
1111 offsetof(struct ethtool_rxfh, rss_config[0]),
1112 rss_config, total_size)) {
1113 ret = -EFAULT;
1114 }
1115out:
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301116 kfree(rss_config);
1117
1118 return ret;
1119}
1120
1121static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
1122 void __user *useraddr)
1123{
1124 int ret;
1125 const struct ethtool_ops *ops = dev->ethtool_ops;
1126 struct ethtool_rxnfc rx_rings;
Ben Hutchingsf062a382014-05-15 16:28:07 +01001127 struct ethtool_rxfh rxfh;
1128 u32 dev_indir_size = 0, dev_key_size = 0, i;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301129 u32 *indir = NULL, indir_bytes = 0;
1130 u8 *hkey = NULL;
1131 u8 *rss_config;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301132 u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
Edward Cree84a1d9c42018-03-08 15:45:03 +00001133 bool delete = false;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301134
Eyal Perry892311f2014-12-02 18:12:10 +02001135 if (!ops->get_rxnfc || !ops->set_rxfh)
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301136 return -EOPNOTSUPP;
1137
1138 if (ops->get_rxfh_indir_size)
1139 dev_indir_size = ops->get_rxfh_indir_size(dev);
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301140 if (ops->get_rxfh_key_size)
Dan Carpenterd340c862015-02-20 13:54:05 +03001141 dev_key_size = ops->get_rxfh_key_size(dev);
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301142
Ben Hutchingsf062a382014-05-15 16:28:07 +01001143 if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301144 return -EFAULT;
1145
Ben Hutchingsf062a382014-05-15 16:28:07 +01001146 /* Check that reserved fields are 0 for now */
Edward Cree84a1d9c42018-03-08 15:45:03 +00001147 if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32)
Ben Hutchingsf062a382014-05-15 16:28:07 +01001148 return -EINVAL;
Edward Cree84a1d9c42018-03-08 15:45:03 +00001149 /* Most drivers don't handle rss_context, check it's 0 as well */
1150 if (rxfh.rss_context && !ops->set_rxfh_context)
1151 return -EOPNOTSUPP;
Ben Hutchingsf062a382014-05-15 16:28:07 +01001152
Eyal Perry892311f2014-12-02 18:12:10 +02001153 /* If either indir, hash key or function is valid, proceed further.
1154 * Must request at least one change: indir size, hash key or function.
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301155 */
Ben Hutchingsf062a382014-05-15 16:28:07 +01001156 if ((rxfh.indir_size &&
1157 rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
1158 rxfh.indir_size != dev_indir_size) ||
1159 (rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
1160 (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
Eyal Perry892311f2014-12-02 18:12:10 +02001161 rxfh.key_size == 0 && rxfh.hfunc == ETH_RSS_HASH_NO_CHANGE))
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301162 return -EINVAL;
1163
Ben Hutchingsf062a382014-05-15 16:28:07 +01001164 if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301165 indir_bytes = dev_indir_size * sizeof(indir[0]);
1166
Ben Hutchingsf062a382014-05-15 16:28:07 +01001167 rss_config = kzalloc(indir_bytes + rxfh.key_size, GFP_USER);
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301168 if (!rss_config)
1169 return -ENOMEM;
1170
1171 rx_rings.cmd = ETHTOOL_GRXRINGS;
1172 ret = ops->get_rxnfc(dev, &rx_rings, NULL);
1173 if (ret)
1174 goto out;
1175
Edward Cree84a1d9c42018-03-08 15:45:03 +00001176 /* rxfh.indir_size == 0 means reset the indir table to default (master
1177 * context) or delete the context (other RSS contexts).
Ben Hutchingsf062a382014-05-15 16:28:07 +01001178 * rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged.
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301179 */
Ben Hutchingsf062a382014-05-15 16:28:07 +01001180 if (rxfh.indir_size &&
1181 rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) {
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301182 indir = (u32 *)rss_config;
1183 ret = ethtool_copy_validate_indir(indir,
1184 useraddr + rss_cfg_offset,
1185 &rx_rings,
Ben Hutchingsf062a382014-05-15 16:28:07 +01001186 rxfh.indir_size);
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301187 if (ret)
1188 goto out;
Ben Hutchingsf062a382014-05-15 16:28:07 +01001189 } else if (rxfh.indir_size == 0) {
Edward Cree84a1d9c42018-03-08 15:45:03 +00001190 if (rxfh.rss_context == 0) {
1191 indir = (u32 *)rss_config;
1192 for (i = 0; i < dev_indir_size; i++)
1193 indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
1194 } else {
1195 delete = true;
1196 }
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301197 }
1198
Ben Hutchingsf062a382014-05-15 16:28:07 +01001199 if (rxfh.key_size) {
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301200 hkey = rss_config + indir_bytes;
1201 if (copy_from_user(hkey,
1202 useraddr + rss_cfg_offset + indir_bytes,
Ben Hutchingsf062a382014-05-15 16:28:07 +01001203 rxfh.key_size)) {
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301204 ret = -EFAULT;
1205 goto out;
1206 }
1207 }
1208
Edward Cree84a1d9c42018-03-08 15:45:03 +00001209 if (rxfh.rss_context)
1210 ret = ops->set_rxfh_context(dev, indir, hkey, rxfh.hfunc,
1211 &rxfh.rss_context, delete);
1212 else
1213 ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
Keller, Jacob Ed4ab4282016-02-08 16:05:03 -08001214 if (ret)
1215 goto out;
1216
Edward Cree84a1d9c42018-03-08 15:45:03 +00001217 if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context),
1218 &rxfh.rss_context, sizeof(rxfh.rss_context)))
1219 ret = -EFAULT;
1220
1221 if (!rxfh.rss_context) {
1222 /* indicate whether rxfh was set to default */
1223 if (rxfh.indir_size == 0)
1224 dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
1225 else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
1226 dev->priv_flags |= IFF_RXFH_CONFIGURED;
1227 }
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05301228
1229out:
1230 kfree(rss_config);
1231 return ret;
1232}
1233
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
1235{
1236 struct ethtool_regs regs;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001237 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 void *regbuf;
1239 int reglen, ret;
1240
1241 if (!ops->get_regs || !ops->get_regs_len)
1242 return -EOPNOTSUPP;
1243
1244 if (copy_from_user(&regs, useraddr, sizeof(regs)))
1245 return -EFAULT;
1246
1247 reglen = ops->get_regs_len(dev);
Yunsheng Linf9fc54d2018-12-26 19:51:46 +08001248 if (reglen <= 0)
1249 return reglen;
1250
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 if (regs.len > reglen)
1252 regs.len = reglen;
1253
Dan Carpenteref76c772019-02-01 11:24:06 +03001254 regbuf = vzalloc(reglen);
1255 if (!regbuf)
1256 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257
Vivien Didelot0ee4e762019-06-03 16:57:13 -04001258 if (regs.len < reglen)
1259 reglen = regs.len;
1260
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 ops->get_regs(dev, &regs, regbuf);
1262
1263 ret = -EFAULT;
1264 if (copy_to_user(useraddr, &regs, sizeof(regs)))
1265 goto out;
1266 useraddr += offsetof(struct ethtool_regs, data);
Vivien Didelot0ee4e762019-06-03 16:57:13 -04001267 if (copy_to_user(useraddr, regbuf, reglen))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 goto out;
1269 ret = 0;
1270
1271 out:
Ben Hutchingsa77f5db2010-09-20 08:42:17 +00001272 vfree(regbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 return ret;
1274}
1275
Ben Hutchingsd73d3a82009-10-05 10:59:58 +00001276static int ethtool_reset(struct net_device *dev, char __user *useraddr)
1277{
1278 struct ethtool_value reset;
1279 int ret;
1280
1281 if (!dev->ethtool_ops->reset)
1282 return -EOPNOTSUPP;
1283
1284 if (copy_from_user(&reset, useraddr, sizeof(reset)))
1285 return -EFAULT;
1286
1287 ret = dev->ethtool_ops->reset(dev, &reset.data);
1288 if (ret)
1289 return ret;
1290
1291 if (copy_to_user(useraddr, &reset, sizeof(reset)))
1292 return -EFAULT;
1293 return 0;
1294}
1295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
1297{
zhanglin5ff223e82019-10-26 15:54:16 +08001298 struct ethtool_wolinfo wol;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299
1300 if (!dev->ethtool_ops->get_wol)
1301 return -EOPNOTSUPP;
1302
zhanglin5ff223e82019-10-26 15:54:16 +08001303 memset(&wol, 0, sizeof(struct ethtool_wolinfo));
1304 wol.cmd = ETHTOOL_GWOL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 dev->ethtool_ops->get_wol(dev, &wol);
1306
1307 if (copy_to_user(useraddr, &wol, sizeof(wol)))
1308 return -EFAULT;
1309 return 0;
1310}
1311
1312static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
1313{
1314 struct ethtool_wolinfo wol;
Heiner Kallweit61941142018-09-24 21:58:59 +02001315 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
1317 if (!dev->ethtool_ops->set_wol)
1318 return -EOPNOTSUPP;
1319
1320 if (copy_from_user(&wol, useraddr, sizeof(wol)))
1321 return -EFAULT;
1322
Heiner Kallweit61941142018-09-24 21:58:59 +02001323 ret = dev->ethtool_ops->set_wol(dev, &wol);
1324 if (ret)
1325 return ret;
1326
1327 dev->wol_enabled = !!wol.wolopts;
Michal Kubecek67bffa72020-01-26 23:11:19 +01001328 ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL);
Heiner Kallweit61941142018-09-24 21:58:59 +02001329
1330 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331}
1332
Yuval Mintz80f12ec2012-06-06 17:13:06 +00001333static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)
1334{
1335 struct ethtool_eee edata;
1336 int rc;
1337
1338 if (!dev->ethtool_ops->get_eee)
1339 return -EOPNOTSUPP;
1340
1341 memset(&edata, 0, sizeof(struct ethtool_eee));
1342 edata.cmd = ETHTOOL_GEEE;
1343 rc = dev->ethtool_ops->get_eee(dev, &edata);
1344
1345 if (rc)
1346 return rc;
1347
1348 if (copy_to_user(useraddr, &edata, sizeof(edata)))
1349 return -EFAULT;
1350
1351 return 0;
1352}
1353
1354static int ethtool_set_eee(struct net_device *dev, char __user *useraddr)
1355{
1356 struct ethtool_eee edata;
Michal Kubecek6c5bc8f2020-03-28 00:01:48 +01001357 int ret;
Yuval Mintz80f12ec2012-06-06 17:13:06 +00001358
1359 if (!dev->ethtool_ops->set_eee)
1360 return -EOPNOTSUPP;
1361
1362 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1363 return -EFAULT;
1364
Michal Kubecek6c5bc8f2020-03-28 00:01:48 +01001365 ret = dev->ethtool_ops->set_eee(dev, &edata);
1366 if (!ret)
1367 ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL);
1368 return ret;
Yuval Mintz80f12ec2012-06-06 17:13:06 +00001369}
1370
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371static int ethtool_nway_reset(struct net_device *dev)
1372{
1373 if (!dev->ethtool_ops->nway_reset)
1374 return -EOPNOTSUPP;
1375
1376 return dev->ethtool_ops->nway_reset(dev);
1377}
1378
Ben Hutchingse596e6e2010-12-09 12:08:35 +00001379static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
1380{
1381 struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
Michal Kubecek3d2b8472019-12-27 15:56:23 +01001382 int link = __ethtool_get_link(dev);
Ben Hutchingse596e6e2010-12-09 12:08:35 +00001383
Michal Kubecek3d2b8472019-12-27 15:56:23 +01001384 if (link < 0)
1385 return link;
Ben Hutchingse596e6e2010-12-09 12:08:35 +00001386
Michal Kubecek3d2b8472019-12-27 15:56:23 +01001387 edata.data = link;
Ben Hutchingse596e6e2010-12-09 12:08:35 +00001388 if (copy_to_user(useraddr, &edata, sizeof(edata)))
1389 return -EFAULT;
1390 return 0;
1391}
1392
Ben Hutchings081d0942012-04-12 00:42:12 +00001393static int ethtool_get_any_eeprom(struct net_device *dev, void __user *useraddr,
1394 int (*getter)(struct net_device *,
1395 struct ethtool_eeprom *, u8 *),
1396 u32 total_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397{
1398 struct ethtool_eeprom eeprom;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001399 void __user *userbuf = useraddr + sizeof(eeprom);
1400 u32 bytes_remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 u8 *data;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001402 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
1405 return -EFAULT;
1406
1407 /* Check for wrap and zero */
1408 if (eeprom.offset + eeprom.len <= eeprom.offset)
1409 return -EINVAL;
1410
1411 /* Check for exceeding total eeprom len */
Ben Hutchings081d0942012-04-12 00:42:12 +00001412 if (eeprom.offset + eeprom.len > total_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 return -EINVAL;
1414
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001415 data = kmalloc(PAGE_SIZE, GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 if (!data)
1417 return -ENOMEM;
1418
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001419 bytes_remaining = eeprom.len;
1420 while (bytes_remaining > 0) {
1421 eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422
Ben Hutchings081d0942012-04-12 00:42:12 +00001423 ret = getter(dev, &eeprom, data);
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001424 if (ret)
1425 break;
1426 if (copy_to_user(userbuf, data, eeprom.len)) {
1427 ret = -EFAULT;
1428 break;
1429 }
1430 userbuf += eeprom.len;
1431 eeprom.offset += eeprom.len;
1432 bytes_remaining -= eeprom.len;
1433 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
Mandeep Singh Bainesc5835df2008-04-24 20:55:56 -07001435 eeprom.len = userbuf - (useraddr + sizeof(eeprom));
1436 eeprom.offset -= eeprom.len;
1437 if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
1438 ret = -EFAULT;
1439
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 kfree(data);
1441 return ret;
1442}
1443
Ben Hutchings081d0942012-04-12 00:42:12 +00001444static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
1445{
1446 const struct ethtool_ops *ops = dev->ethtool_ops;
1447
Guenter Roecke0fb6fb2014-10-30 20:50:15 -07001448 if (!ops->get_eeprom || !ops->get_eeprom_len ||
1449 !ops->get_eeprom_len(dev))
Ben Hutchings081d0942012-04-12 00:42:12 +00001450 return -EOPNOTSUPP;
1451
1452 return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom,
1453 ops->get_eeprom_len(dev));
1454}
1455
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
1457{
1458 struct ethtool_eeprom eeprom;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001459 const struct ethtool_ops *ops = dev->ethtool_ops;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001460 void __user *userbuf = useraddr + sizeof(eeprom);
1461 u32 bytes_remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 u8 *data;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001463 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
Guenter Roecke0fb6fb2014-10-30 20:50:15 -07001465 if (!ops->set_eeprom || !ops->get_eeprom_len ||
1466 !ops->get_eeprom_len(dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 return -EOPNOTSUPP;
1468
1469 if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
1470 return -EFAULT;
1471
1472 /* Check for wrap and zero */
1473 if (eeprom.offset + eeprom.len <= eeprom.offset)
1474 return -EINVAL;
1475
1476 /* Check for exceeding total eeprom len */
1477 if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
1478 return -EINVAL;
1479
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001480 data = kmalloc(PAGE_SIZE, GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 if (!data)
1482 return -ENOMEM;
1483
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001484 bytes_remaining = eeprom.len;
1485 while (bytes_remaining > 0) {
1486 eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001488 if (copy_from_user(data, userbuf, eeprom.len)) {
1489 ret = -EFAULT;
1490 break;
1491 }
1492 ret = ops->set_eeprom(dev, &eeprom, data);
1493 if (ret)
1494 break;
1495 userbuf += eeprom.len;
1496 eeprom.offset += eeprom.len;
1497 bytes_remaining -= eeprom.len;
1498 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 kfree(data);
1501 return ret;
1502}
1503
chavey97f8aef2010-04-07 21:54:42 -07001504static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev,
1505 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506{
Roland Dreier8e557422010-02-11 12:14:23 -08001507 struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
1509 if (!dev->ethtool_ops->get_coalesce)
1510 return -EOPNOTSUPP;
1511
1512 dev->ethtool_ops->get_coalesce(dev, &coalesce);
1513
1514 if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
1515 return -EFAULT;
1516 return 0;
1517}
1518
Jakub Kicinski95cddcb2020-03-04 21:15:31 -08001519static bool
1520ethtool_set_coalesce_supported(struct net_device *dev,
1521 struct ethtool_coalesce *coalesce)
1522{
1523 u32 supported_params = dev->ethtool_ops->supported_coalesce_params;
1524 u32 nonzero_params = 0;
1525
Jakub Kicinski95cddcb2020-03-04 21:15:31 -08001526 if (coalesce->rx_coalesce_usecs)
1527 nonzero_params |= ETHTOOL_COALESCE_RX_USECS;
1528 if (coalesce->rx_max_coalesced_frames)
1529 nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES;
1530 if (coalesce->rx_coalesce_usecs_irq)
1531 nonzero_params |= ETHTOOL_COALESCE_RX_USECS_IRQ;
1532 if (coalesce->rx_max_coalesced_frames_irq)
1533 nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ;
1534 if (coalesce->tx_coalesce_usecs)
1535 nonzero_params |= ETHTOOL_COALESCE_TX_USECS;
1536 if (coalesce->tx_max_coalesced_frames)
1537 nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES;
1538 if (coalesce->tx_coalesce_usecs_irq)
1539 nonzero_params |= ETHTOOL_COALESCE_TX_USECS_IRQ;
1540 if (coalesce->tx_max_coalesced_frames_irq)
1541 nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ;
1542 if (coalesce->stats_block_coalesce_usecs)
1543 nonzero_params |= ETHTOOL_COALESCE_STATS_BLOCK_USECS;
1544 if (coalesce->use_adaptive_rx_coalesce)
1545 nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_RX;
1546 if (coalesce->use_adaptive_tx_coalesce)
1547 nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_TX;
1548 if (coalesce->pkt_rate_low)
1549 nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_LOW;
1550 if (coalesce->rx_coalesce_usecs_low)
1551 nonzero_params |= ETHTOOL_COALESCE_RX_USECS_LOW;
1552 if (coalesce->rx_max_coalesced_frames_low)
1553 nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW;
1554 if (coalesce->tx_coalesce_usecs_low)
1555 nonzero_params |= ETHTOOL_COALESCE_TX_USECS_LOW;
1556 if (coalesce->tx_max_coalesced_frames_low)
1557 nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW;
1558 if (coalesce->pkt_rate_high)
1559 nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_HIGH;
1560 if (coalesce->rx_coalesce_usecs_high)
1561 nonzero_params |= ETHTOOL_COALESCE_RX_USECS_HIGH;
1562 if (coalesce->rx_max_coalesced_frames_high)
1563 nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH;
1564 if (coalesce->tx_coalesce_usecs_high)
1565 nonzero_params |= ETHTOOL_COALESCE_TX_USECS_HIGH;
1566 if (coalesce->tx_max_coalesced_frames_high)
1567 nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH;
1568 if (coalesce->rate_sample_interval)
1569 nonzero_params |= ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL;
1570
1571 return (supported_params & nonzero_params) == nonzero_params;
1572}
1573
chavey97f8aef2010-04-07 21:54:42 -07001574static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
1575 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576{
1577 struct ethtool_coalesce coalesce;
Michal Kubecek0cf3eac2020-03-28 00:01:18 +01001578 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
David S. Millerfa04ae52005-06-06 15:07:19 -07001580 if (!dev->ethtool_ops->set_coalesce)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581 return -EOPNOTSUPP;
1582
1583 if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
1584 return -EFAULT;
1585
Jakub Kicinski95cddcb2020-03-04 21:15:31 -08001586 if (!ethtool_set_coalesce_supported(dev, &coalesce))
1587 return -EOPNOTSUPP;
1588
Michal Kubecek0cf3eac2020-03-28 00:01:18 +01001589 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce);
1590 if (!ret)
1591 ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL);
1592 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593}
1594
1595static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
1596{
Roland Dreier8e557422010-02-11 12:14:23 -08001597 struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
1599 if (!dev->ethtool_ops->get_ringparam)
1600 return -EOPNOTSUPP;
1601
1602 dev->ethtool_ops->get_ringparam(dev, &ringparam);
1603
1604 if (copy_to_user(useraddr, &ringparam, sizeof(ringparam)))
1605 return -EFAULT;
1606 return 0;
1607}
1608
1609static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
1610{
Eugenia Emantayev37e2d992018-01-08 16:00:24 +02001611 struct ethtool_ringparam ringparam, max = { .cmd = ETHTOOL_GRINGPARAM };
Michal Kubecekbc9d1c92020-03-12 21:08:33 +01001612 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
Eugenia Emantayev37e2d992018-01-08 16:00:24 +02001614 if (!dev->ethtool_ops->set_ringparam || !dev->ethtool_ops->get_ringparam)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 return -EOPNOTSUPP;
1616
1617 if (copy_from_user(&ringparam, useraddr, sizeof(ringparam)))
1618 return -EFAULT;
1619
Eugenia Emantayev37e2d992018-01-08 16:00:24 +02001620 dev->ethtool_ops->get_ringparam(dev, &max);
1621
1622 /* ensure new ring parameters are within the maximums */
1623 if (ringparam.rx_pending > max.rx_max_pending ||
1624 ringparam.rx_mini_pending > max.rx_mini_max_pending ||
1625 ringparam.rx_jumbo_pending > max.rx_jumbo_max_pending ||
1626 ringparam.tx_pending > max.tx_max_pending)
1627 return -EINVAL;
1628
Michal Kubecekbc9d1c92020-03-12 21:08:33 +01001629 ret = dev->ethtool_ops->set_ringparam(dev, &ringparam);
1630 if (!ret)
1631 ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL);
1632 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633}
1634
amit salecha8b5933c2011-04-07 01:58:42 +00001635static noinline_for_stack int ethtool_get_channels(struct net_device *dev,
1636 void __user *useraddr)
1637{
1638 struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS };
1639
1640 if (!dev->ethtool_ops->get_channels)
1641 return -EOPNOTSUPP;
1642
1643 dev->ethtool_ops->get_channels(dev, &channels);
1644
1645 if (copy_to_user(useraddr, &channels, sizeof(channels)))
1646 return -EFAULT;
1647 return 0;
1648}
1649
1650static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
1651 void __user *useraddr)
1652{
Jakub Kicinskib8c8a2e2018-10-01 14:51:35 +02001653 struct ethtool_channels channels, curr = { .cmd = ETHTOOL_GCHANNELS };
Jakub Kicinski1661d342018-10-01 14:51:36 +02001654 u16 from_channel, to_channel;
Keller, Jacob Ed4ab4282016-02-08 16:05:03 -08001655 u32 max_rx_in_use = 0;
Jakub Kicinski1661d342018-10-01 14:51:36 +02001656 unsigned int i;
Michal Kubecek546379b2020-03-12 21:08:48 +01001657 int ret;
amit salecha8b5933c2011-04-07 01:58:42 +00001658
Keller, Jacob E8bf36862016-02-08 16:05:04 -08001659 if (!dev->ethtool_ops->set_channels || !dev->ethtool_ops->get_channels)
amit salecha8b5933c2011-04-07 01:58:42 +00001660 return -EOPNOTSUPP;
1661
1662 if (copy_from_user(&channels, useraddr, sizeof(channels)))
1663 return -EFAULT;
1664
Jakub Kicinskib8c8a2e2018-10-01 14:51:35 +02001665 dev->ethtool_ops->get_channels(dev, &curr);
Keller, Jacob E8bf36862016-02-08 16:05:04 -08001666
1667 /* ensure new counts are within the maximums */
Jakub Kicinskib8c8a2e2018-10-01 14:51:35 +02001668 if (channels.rx_count > curr.max_rx ||
1669 channels.tx_count > curr.max_tx ||
1670 channels.combined_count > curr.max_combined ||
1671 channels.other_count > curr.max_other)
Keller, Jacob E8bf36862016-02-08 16:05:04 -08001672 return -EINVAL;
1673
Keller, Jacob Ed4ab4282016-02-08 16:05:03 -08001674 /* ensure the new Rx count fits within the configured Rx flow
1675 * indirection table settings */
1676 if (netif_is_rxfh_configured(dev) &&
1677 !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) &&
1678 (channels.combined_count + channels.rx_count) <= max_rx_in_use)
1679 return -EINVAL;
1680
Jakub Kicinski1661d342018-10-01 14:51:36 +02001681 /* Disabling channels, query zero-copy AF_XDP sockets */
1682 from_channel = channels.combined_count +
1683 min(channels.rx_count, channels.tx_count);
1684 to_channel = curr.combined_count + max(curr.rx_count, curr.tx_count);
1685 for (i = from_channel; i < to_channel; i++)
1686 if (xdp_get_umem_from_qid(dev, i))
1687 return -EINVAL;
1688
Michal Kubecek546379b2020-03-12 21:08:48 +01001689 ret = dev->ethtool_ops->set_channels(dev, &channels);
1690 if (!ret)
1691 ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF, NULL);
1692 return ret;
amit salecha8b5933c2011-04-07 01:58:42 +00001693}
1694
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
1696{
Li RongQinge83887f2019-02-27 20:47:57 +08001697 struct ethtool_pauseparam pauseparam = { .cmd = ETHTOOL_GPAUSEPARAM };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698
1699 if (!dev->ethtool_ops->get_pauseparam)
1700 return -EOPNOTSUPP;
1701
1702 dev->ethtool_ops->get_pauseparam(dev, &pauseparam);
1703
1704 if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam)))
1705 return -EFAULT;
1706 return 0;
1707}
1708
1709static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
1710{
1711 struct ethtool_pauseparam pauseparam;
Michal Kubecekbf37faa2020-03-28 00:01:33 +01001712 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713
Jeff Garzike1b90c42006-07-17 12:54:40 -04001714 if (!dev->ethtool_ops->set_pauseparam)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 return -EOPNOTSUPP;
1716
1717 if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))
1718 return -EFAULT;
1719
Michal Kubecekbf37faa2020-03-28 00:01:33 +01001720 ret = dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
1721 if (!ret)
1722 ethtool_notify(dev, ETHTOOL_MSG_PAUSE_NTF, NULL);
1723 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724}
1725
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
1727{
1728 struct ethtool_test test;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001729 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 u64 *data;
Jeff Garzikff03d492007-08-15 16:01:08 -07001731 int ret, test_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001733 if (!ops->self_test || !ops->get_sset_count)
Jeff Garzikff03d492007-08-15 16:01:08 -07001734 return -EOPNOTSUPP;
1735
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001736 test_len = ops->get_sset_count(dev, ETH_SS_TEST);
Jeff Garzikff03d492007-08-15 16:01:08 -07001737 if (test_len < 0)
1738 return test_len;
1739 WARN_ON(test_len == 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740
1741 if (copy_from_user(&test, useraddr, sizeof(test)))
1742 return -EFAULT;
1743
Jeff Garzikff03d492007-08-15 16:01:08 -07001744 test.len = test_len;
Kees Cook6da2ec52018-06-12 13:55:00 -07001745 data = kmalloc_array(test_len, sizeof(u64), GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 if (!data)
1747 return -ENOMEM;
1748
1749 ops->self_test(dev, &test, data);
1750
1751 ret = -EFAULT;
1752 if (copy_to_user(useraddr, &test, sizeof(test)))
1753 goto out;
1754 useraddr += sizeof(test);
1755 if (copy_to_user(useraddr, data, test.len * sizeof(u64)))
1756 goto out;
1757 ret = 0;
1758
1759 out:
1760 kfree(data);
1761 return ret;
1762}
1763
1764static int ethtool_get_strings(struct net_device *dev, void __user *useraddr)
1765{
1766 struct ethtool_gstrings gstrings;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767 u8 *data;
1768 int ret;
1769
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
1771 return -EFAULT;
1772
Michał Mirosław340ae162011-02-15 16:59:16 +00001773 ret = __ethtool_get_sset_count(dev, gstrings.string_set);
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001774 if (ret < 0)
1775 return ret;
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001776 if (ret > S32_MAX / ETH_GSTRING_LEN)
1777 return -ENOMEM;
1778 WARN_ON_ONCE(!ret);
Jeff Garzikff03d492007-08-15 16:01:08 -07001779
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001780 gstrings.len = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781
Li RongQing3d883022019-03-29 09:18:02 +08001782 if (gstrings.len) {
1783 data = vzalloc(array_size(gstrings.len, ETH_GSTRING_LEN));
1784 if (!data)
1785 return -ENOMEM;
1786
1787 __ethtool_get_strings(dev, gstrings.string_set, data);
1788 } else {
1789 data = NULL;
1790 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
1792 ret = -EFAULT;
1793 if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
1794 goto out;
1795 useraddr += sizeof(gstrings);
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001796 if (gstrings.len &&
1797 copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 goto out;
1799 ret = 0;
1800
Michał Mirosław340ae162011-02-15 16:59:16 +00001801out:
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001802 vfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 return ret;
1804}
1805
1806static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
1807{
1808 struct ethtool_value id;
Ben Hutchings68f512f2011-04-02 00:35:15 +01001809 static bool busy;
Jiri Pirkoc03a14e2013-01-07 09:02:08 +00001810 const struct ethtool_ops *ops = dev->ethtool_ops;
Ben Hutchings68f512f2011-04-02 00:35:15 +01001811 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
Jiri Pirkoc03a14e2013-01-07 09:02:08 +00001813 if (!ops->set_phys_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 return -EOPNOTSUPP;
1815
Ben Hutchings68f512f2011-04-02 00:35:15 +01001816 if (busy)
1817 return -EBUSY;
1818
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 if (copy_from_user(&id, useraddr, sizeof(id)))
1820 return -EFAULT;
1821
Jiri Pirkoc03a14e2013-01-07 09:02:08 +00001822 rc = ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE);
Allan, Bruce Wfce55922011-04-13 13:09:10 +00001823 if (rc < 0)
Ben Hutchings68f512f2011-04-02 00:35:15 +01001824 return rc;
1825
1826 /* Drop the RTNL lock while waiting, but prevent reentry or
1827 * removal of the device.
1828 */
1829 busy = true;
1830 dev_hold(dev);
1831 rtnl_unlock();
1832
1833 if (rc == 0) {
1834 /* Driver will handle this itself */
1835 schedule_timeout_interruptible(
Allan, Bruce W143780c2011-04-11 13:01:59 +00001836 id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT);
Ben Hutchings68f512f2011-04-02 00:35:15 +01001837 } else {
Allan, Bruce Wfce55922011-04-13 13:09:10 +00001838 /* Driver expects to be called at twice the frequency in rc */
1839 int n = rc * 2, i, interval = HZ / n;
Ben Hutchings68f512f2011-04-02 00:35:15 +01001840
Allan, Bruce Wfce55922011-04-13 13:09:10 +00001841 /* Count down seconds */
1842 do {
1843 /* Count down iterations per second */
1844 i = n;
1845 do {
1846 rtnl_lock();
Jiri Pirkoc03a14e2013-01-07 09:02:08 +00001847 rc = ops->set_phys_id(dev,
Allan, Bruce Wfce55922011-04-13 13:09:10 +00001848 (i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON);
1849 rtnl_unlock();
1850 if (rc)
1851 break;
1852 schedule_timeout_interruptible(interval);
1853 } while (!signal_pending(current) && --i != 0);
Ben Hutchings68f512f2011-04-02 00:35:15 +01001854 } while (!signal_pending(current) &&
1855 (id.data == 0 || --id.data != 0));
1856 }
1857
1858 rtnl_lock();
1859 dev_put(dev);
1860 busy = false;
1861
Jiri Pirkoc03a14e2013-01-07 09:02:08 +00001862 (void) ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE);
Ben Hutchings68f512f2011-04-02 00:35:15 +01001863 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864}
1865
1866static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
1867{
1868 struct ethtool_stats stats;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001869 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 u64 *data;
Jeff Garzikff03d492007-08-15 16:01:08 -07001871 int ret, n_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001873 if (!ops->get_ethtool_stats || !ops->get_sset_count)
Jeff Garzikff03d492007-08-15 16:01:08 -07001874 return -EOPNOTSUPP;
1875
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001876 n_stats = ops->get_sset_count(dev, ETH_SS_STATS);
Jeff Garzikff03d492007-08-15 16:01:08 -07001877 if (n_stats < 0)
1878 return n_stats;
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001879 if (n_stats > S32_MAX / sizeof(u64))
1880 return -ENOMEM;
1881 WARN_ON_ONCE(!n_stats);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882 if (copy_from_user(&stats, useraddr, sizeof(stats)))
1883 return -EFAULT;
1884
Jeff Garzikff03d492007-08-15 16:01:08 -07001885 stats.n_stats = n_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886
Li RongQing3d883022019-03-29 09:18:02 +08001887 if (n_stats) {
1888 data = vzalloc(array_size(n_stats, sizeof(u64)));
1889 if (!data)
1890 return -ENOMEM;
1891 ops->get_ethtool_stats(dev, &stats, data);
1892 } else {
1893 data = NULL;
1894 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895
1896 ret = -EFAULT;
1897 if (copy_to_user(useraddr, &stats, sizeof(stats)))
1898 goto out;
1899 useraddr += sizeof(stats);
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001900 if (n_stats && copy_to_user(useraddr, data, n_stats * sizeof(u64)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 goto out;
1902 ret = 0;
1903
1904 out:
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001905 vfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 return ret;
1907}
1908
Andrew Lunnf3a40942015-12-30 16:28:25 +01001909static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
1910{
Florian Fainelli99943382018-04-25 12:12:48 -07001911 const struct ethtool_ops *ops = dev->ethtool_ops;
Andrew Lunnf3a40942015-12-30 16:28:25 +01001912 struct phy_device *phydev = dev->phydev;
Florian Fainelli99943382018-04-25 12:12:48 -07001913 struct ethtool_stats stats;
Andrew Lunnf3a40942015-12-30 16:28:25 +01001914 u64 *data;
1915 int ret, n_stats;
1916
Florian Fainelli99943382018-04-25 12:12:48 -07001917 if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
Andrew Lunnf3a40942015-12-30 16:28:25 +01001918 return -EOPNOTSUPP;
1919
Florian Fainelli99943382018-04-25 12:12:48 -07001920 if (dev->phydev && !ops->get_ethtool_phy_stats)
1921 n_stats = phy_ethtool_get_sset_count(dev->phydev);
1922 else
1923 n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
Andrew Lunnf3a40942015-12-30 16:28:25 +01001924 if (n_stats < 0)
1925 return n_stats;
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001926 if (n_stats > S32_MAX / sizeof(u64))
1927 return -ENOMEM;
1928 WARN_ON_ONCE(!n_stats);
Andrew Lunnf3a40942015-12-30 16:28:25 +01001929
1930 if (copy_from_user(&stats, useraddr, sizeof(stats)))
1931 return -EFAULT;
1932
1933 stats.n_stats = n_stats;
Andrew Lunnf3a40942015-12-30 16:28:25 +01001934
Li RongQing3d883022019-03-29 09:18:02 +08001935 if (n_stats) {
1936 data = vzalloc(array_size(n_stats, sizeof(u64)));
1937 if (!data)
1938 return -ENOMEM;
1939
1940 if (dev->phydev && !ops->get_ethtool_phy_stats) {
1941 ret = phy_ethtool_get_stats(dev->phydev, &stats, data);
1942 if (ret < 0)
1943 goto out;
1944 } else {
1945 ops->get_ethtool_phy_stats(dev, &stats, data);
1946 }
Florian Fainelli99943382018-04-25 12:12:48 -07001947 } else {
Li RongQing3d883022019-03-29 09:18:02 +08001948 data = NULL;
Florian Fainelli99943382018-04-25 12:12:48 -07001949 }
Andrew Lunnf3a40942015-12-30 16:28:25 +01001950
1951 ret = -EFAULT;
1952 if (copy_to_user(useraddr, &stats, sizeof(stats)))
1953 goto out;
1954 useraddr += sizeof(stats);
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001955 if (n_stats && copy_to_user(useraddr, data, n_stats * sizeof(u64)))
Andrew Lunnf3a40942015-12-30 16:28:25 +01001956 goto out;
1957 ret = 0;
1958
1959 out:
Alexei Starovoitov4d1ceea2017-01-30 18:25:18 -08001960 vfree(data);
Andrew Lunnf3a40942015-12-30 16:28:25 +01001961 return ret;
1962}
1963
viro@ftp.linux.org.uk0bf0519d2005-09-05 03:26:18 +01001964static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
Jon Wetzela6f9a702005-08-20 17:15:54 -07001965{
1966 struct ethtool_perm_addr epaddr;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001967
Matthew Wilcox313674a2007-07-31 14:00:29 -07001968 if (copy_from_user(&epaddr, useraddr, sizeof(epaddr)))
Jon Wetzela6f9a702005-08-20 17:15:54 -07001969 return -EFAULT;
1970
Matthew Wilcox313674a2007-07-31 14:00:29 -07001971 if (epaddr.size < dev->addr_len)
1972 return -ETOOSMALL;
1973 epaddr.size = dev->addr_len;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001974
Jon Wetzela6f9a702005-08-20 17:15:54 -07001975 if (copy_to_user(useraddr, &epaddr, sizeof(epaddr)))
Matthew Wilcox313674a2007-07-31 14:00:29 -07001976 return -EFAULT;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001977 useraddr += sizeof(epaddr);
Matthew Wilcox313674a2007-07-31 14:00:29 -07001978 if (copy_to_user(useraddr, dev->perm_addr, epaddr.size))
1979 return -EFAULT;
1980 return 0;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001981}
1982
Jeff Garzik13c99b22007-08-15 16:01:56 -07001983static int ethtool_get_value(struct net_device *dev, char __user *useraddr,
1984 u32 cmd, u32 (*actor)(struct net_device *))
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001985{
Roland Dreier8e557422010-02-11 12:14:23 -08001986 struct ethtool_value edata = { .cmd = cmd };
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001987
Jeff Garzik13c99b22007-08-15 16:01:56 -07001988 if (!actor)
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001989 return -EOPNOTSUPP;
1990
Jeff Garzik13c99b22007-08-15 16:01:56 -07001991 edata.data = actor(dev);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001992
1993 if (copy_to_user(useraddr, &edata, sizeof(edata)))
1994 return -EFAULT;
1995 return 0;
1996}
1997
Jeff Garzik13c99b22007-08-15 16:01:56 -07001998static int ethtool_set_value_void(struct net_device *dev, char __user *useraddr,
1999 void (*actor)(struct net_device *, u32))
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07002000{
2001 struct ethtool_value edata;
2002
Jeff Garzik13c99b22007-08-15 16:01:56 -07002003 if (!actor)
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07002004 return -EOPNOTSUPP;
2005
2006 if (copy_from_user(&edata, useraddr, sizeof(edata)))
2007 return -EFAULT;
2008
Jeff Garzik13c99b22007-08-15 16:01:56 -07002009 actor(dev, edata.data);
Jeff Garzik339bf022007-08-15 16:01:32 -07002010 return 0;
2011}
2012
Jeff Garzik13c99b22007-08-15 16:01:56 -07002013static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
2014 int (*actor)(struct net_device *, u32))
Jeff Garzik339bf022007-08-15 16:01:32 -07002015{
2016 struct ethtool_value edata;
2017
Jeff Garzik13c99b22007-08-15 16:01:56 -07002018 if (!actor)
Jeff Garzik339bf022007-08-15 16:01:32 -07002019 return -EOPNOTSUPP;
2020
2021 if (copy_from_user(&edata, useraddr, sizeof(edata)))
2022 return -EFAULT;
2023
Jeff Garzik13c99b22007-08-15 16:01:56 -07002024 return actor(dev, edata.data);
Jeff Garzik339bf022007-08-15 16:01:32 -07002025}
2026
chavey97f8aef2010-04-07 21:54:42 -07002027static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
2028 char __user *useraddr)
Ajit Khaparde05c6a8d2009-09-02 17:02:55 +00002029{
2030 struct ethtool_flash efl;
2031
2032 if (copy_from_user(&efl, useraddr, sizeof(efl)))
2033 return -EFAULT;
Ben Hutchings786f5282012-02-01 09:32:25 +00002034 efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
2035
Jakub Kicinski1b45ff62019-02-25 19:34:06 -08002036 if (!dev->ethtool_ops->flash_device)
2037 return devlink_compat_flash_update(dev, efl.data);
Jakub Kicinski4eceba12019-02-14 13:40:45 -08002038
Ajit Khaparde05c6a8d2009-09-02 17:02:55 +00002039 return dev->ethtool_ops->flash_device(dev, &efl);
2040}
2041
Anirban Chakraborty29dd54b2011-05-12 12:48:32 +00002042static int ethtool_set_dump(struct net_device *dev,
2043 void __user *useraddr)
2044{
2045 struct ethtool_dump dump;
2046
2047 if (!dev->ethtool_ops->set_dump)
2048 return -EOPNOTSUPP;
2049
2050 if (copy_from_user(&dump, useraddr, sizeof(dump)))
2051 return -EFAULT;
2052
2053 return dev->ethtool_ops->set_dump(dev, &dump);
2054}
2055
2056static int ethtool_get_dump_flag(struct net_device *dev,
2057 void __user *useraddr)
2058{
2059 int ret;
2060 struct ethtool_dump dump;
2061 const struct ethtool_ops *ops = dev->ethtool_ops;
2062
Jiri Pirkoc03a14e2013-01-07 09:02:08 +00002063 if (!ops->get_dump_flag)
Anirban Chakraborty29dd54b2011-05-12 12:48:32 +00002064 return -EOPNOTSUPP;
2065
2066 if (copy_from_user(&dump, useraddr, sizeof(dump)))
2067 return -EFAULT;
2068
2069 ret = ops->get_dump_flag(dev, &dump);
2070 if (ret)
2071 return ret;
2072
2073 if (copy_to_user(useraddr, &dump, sizeof(dump)))
2074 return -EFAULT;
2075 return 0;
2076}
2077
2078static int ethtool_get_dump_data(struct net_device *dev,
2079 void __user *useraddr)
2080{
2081 int ret;
2082 __u32 len;
2083 struct ethtool_dump dump, tmp;
2084 const struct ethtool_ops *ops = dev->ethtool_ops;
2085 void *data = NULL;
2086
Jiri Pirkoc03a14e2013-01-07 09:02:08 +00002087 if (!ops->get_dump_data || !ops->get_dump_flag)
Anirban Chakraborty29dd54b2011-05-12 12:48:32 +00002088 return -EOPNOTSUPP;
2089
2090 if (copy_from_user(&dump, useraddr, sizeof(dump)))
2091 return -EFAULT;
2092
2093 memset(&tmp, 0, sizeof(tmp));
2094 tmp.cmd = ETHTOOL_GET_DUMP_FLAG;
2095 ret = ops->get_dump_flag(dev, &tmp);
2096 if (ret)
2097 return ret;
2098
Michal Schmidtc590b5e2013-07-01 17:23:30 +02002099 len = min(tmp.len, dump.len);
Anirban Chakraborty29dd54b2011-05-12 12:48:32 +00002100 if (!len)
2101 return -EFAULT;
2102
Michal Schmidtc590b5e2013-07-01 17:23:30 +02002103 /* Don't ever let the driver think there's more space available
2104 * than it requested with .get_dump_flag().
2105 */
2106 dump.len = len;
2107
2108 /* Always allocate enough space to hold the whole thing so that the
2109 * driver does not need to check the length and bother with partial
2110 * dumping.
2111 */
Anirban Chakraborty29dd54b2011-05-12 12:48:32 +00002112 data = vzalloc(tmp.len);
2113 if (!data)
2114 return -ENOMEM;
2115 ret = ops->get_dump_data(dev, &dump, data);
2116 if (ret)
2117 goto out;
2118
Michal Schmidtc590b5e2013-07-01 17:23:30 +02002119 /* There are two sane possibilities:
2120 * 1. The driver's .get_dump_data() does not touch dump.len.
2121 * 2. Or it may set dump.len to how much it really writes, which
2122 * should be tmp.len (or len if it can do a partial dump).
2123 * In any case respond to userspace with the actual length of data
2124 * it's receiving.
2125 */
2126 WARN_ON(dump.len != len && dump.len != tmp.len);
2127 dump.len = len;
2128
Anirban Chakraborty29dd54b2011-05-12 12:48:32 +00002129 if (copy_to_user(useraddr, &dump, sizeof(dump))) {
2130 ret = -EFAULT;
2131 goto out;
2132 }
2133 useraddr += offsetof(struct ethtool_dump, data);
2134 if (copy_to_user(useraddr, data, len))
2135 ret = -EFAULT;
2136out:
2137 vfree(data);
2138 return ret;
2139}
2140
Richard Cochranc8f3a8c2012-04-03 22:59:17 +00002141static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr)
2142{
Richard Cochranc8f3a8c2012-04-03 22:59:17 +00002143 struct ethtool_ts_info info;
Michal Kubecek5b071c52020-03-28 00:01:58 +01002144 int err;
Richard Cochranc8f3a8c2012-04-03 22:59:17 +00002145
Michal Kubecek5b071c52020-03-28 00:01:58 +01002146 err = __ethtool_get_ts_info(dev, &info);
Richard Cochranc8f3a8c2012-04-03 22:59:17 +00002147 if (err)
2148 return err;
2149
2150 if (copy_to_user(useraddr, &info, sizeof(info)))
Michal Kubecek5b071c52020-03-28 00:01:58 +01002151 return -EFAULT;
Richard Cochranc8f3a8c2012-04-03 22:59:17 +00002152
Michal Kubecek5b071c52020-03-28 00:01:58 +01002153 return 0;
Richard Cochranc8f3a8c2012-04-03 22:59:17 +00002154}
2155
Ed Swierk2f438362015-01-02 17:27:56 -08002156static int __ethtool_get_module_info(struct net_device *dev,
2157 struct ethtool_modinfo *modinfo)
2158{
2159 const struct ethtool_ops *ops = dev->ethtool_ops;
2160 struct phy_device *phydev = dev->phydev;
2161
Russell Kinge679c9c2018-03-28 15:44:16 -07002162 if (dev->sfp_bus)
2163 return sfp_get_module_info(dev->sfp_bus, modinfo);
2164
Ed Swierk2f438362015-01-02 17:27:56 -08002165 if (phydev && phydev->drv && phydev->drv->module_info)
2166 return phydev->drv->module_info(phydev, modinfo);
2167
2168 if (ops->get_module_info)
2169 return ops->get_module_info(dev, modinfo);
2170
2171 return -EOPNOTSUPP;
2172}
2173
Stuart Hodgson41c3cb62012-04-19 09:44:42 +01002174static int ethtool_get_module_info(struct net_device *dev,
2175 void __user *useraddr)
2176{
2177 int ret;
2178 struct ethtool_modinfo modinfo;
Stuart Hodgson41c3cb62012-04-19 09:44:42 +01002179
2180 if (copy_from_user(&modinfo, useraddr, sizeof(modinfo)))
2181 return -EFAULT;
2182
Ed Swierk2f438362015-01-02 17:27:56 -08002183 ret = __ethtool_get_module_info(dev, &modinfo);
Stuart Hodgson41c3cb62012-04-19 09:44:42 +01002184 if (ret)
2185 return ret;
2186
2187 if (copy_to_user(useraddr, &modinfo, sizeof(modinfo)))
2188 return -EFAULT;
2189
2190 return 0;
2191}
2192
Ed Swierk2f438362015-01-02 17:27:56 -08002193static int __ethtool_get_module_eeprom(struct net_device *dev,
2194 struct ethtool_eeprom *ee, u8 *data)
2195{
2196 const struct ethtool_ops *ops = dev->ethtool_ops;
2197 struct phy_device *phydev = dev->phydev;
2198
Russell Kinge679c9c2018-03-28 15:44:16 -07002199 if (dev->sfp_bus)
2200 return sfp_get_module_eeprom(dev->sfp_bus, ee, data);
2201
Ed Swierk2f438362015-01-02 17:27:56 -08002202 if (phydev && phydev->drv && phydev->drv->module_eeprom)
2203 return phydev->drv->module_eeprom(phydev, ee, data);
2204
2205 if (ops->get_module_eeprom)
2206 return ops->get_module_eeprom(dev, ee, data);
2207
2208 return -EOPNOTSUPP;
2209}
2210
Stuart Hodgson41c3cb62012-04-19 09:44:42 +01002211static int ethtool_get_module_eeprom(struct net_device *dev,
2212 void __user *useraddr)
2213{
2214 int ret;
2215 struct ethtool_modinfo modinfo;
Stuart Hodgson41c3cb62012-04-19 09:44:42 +01002216
Ed Swierk2f438362015-01-02 17:27:56 -08002217 ret = __ethtool_get_module_info(dev, &modinfo);
Stuart Hodgson41c3cb62012-04-19 09:44:42 +01002218 if (ret)
2219 return ret;
2220
Ed Swierk2f438362015-01-02 17:27:56 -08002221 return ethtool_get_any_eeprom(dev, useraddr,
2222 __ethtool_get_module_eeprom,
Stuart Hodgson41c3cb62012-04-19 09:44:42 +01002223 modinfo.eeprom_len);
2224}
2225
Govindarajulu Varadarajanf0db9b02014-09-03 03:17:20 +05302226static int ethtool_tunable_valid(const struct ethtool_tunable *tuna)
2227{
2228 switch (tuna->id) {
2229 case ETHTOOL_RX_COPYBREAK:
Eric Dumazet1255a502014-10-05 12:35:21 +03002230 case ETHTOOL_TX_COPYBREAK:
Govindarajulu Varadarajanf0db9b02014-09-03 03:17:20 +05302231 if (tuna->len != sizeof(u32) ||
2232 tuna->type_id != ETHTOOL_TUNABLE_U32)
2233 return -EINVAL;
2234 break;
Inbar Karmye1577c12017-11-20 16:14:30 +02002235 case ETHTOOL_PFC_PREVENTION_TOUT:
2236 if (tuna->len != sizeof(u16) ||
2237 tuna->type_id != ETHTOOL_TUNABLE_U16)
2238 return -EINVAL;
2239 break;
Govindarajulu Varadarajanf0db9b02014-09-03 03:17:20 +05302240 default:
2241 return -EINVAL;
2242 }
2243
2244 return 0;
2245}
2246
2247static int ethtool_get_tunable(struct net_device *dev, void __user *useraddr)
2248{
2249 int ret;
2250 struct ethtool_tunable tuna;
2251 const struct ethtool_ops *ops = dev->ethtool_ops;
2252 void *data;
2253
2254 if (!ops->get_tunable)
2255 return -EOPNOTSUPP;
2256 if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
2257 return -EFAULT;
2258 ret = ethtool_tunable_valid(&tuna);
2259 if (ret)
2260 return ret;
2261 data = kmalloc(tuna.len, GFP_USER);
2262 if (!data)
2263 return -ENOMEM;
2264 ret = ops->get_tunable(dev, &tuna, data);
2265 if (ret)
2266 goto out;
2267 useraddr += sizeof(tuna);
2268 ret = -EFAULT;
2269 if (copy_to_user(useraddr, data, tuna.len))
2270 goto out;
2271 ret = 0;
2272
2273out:
2274 kfree(data);
2275 return ret;
2276}
2277
2278static int ethtool_set_tunable(struct net_device *dev, void __user *useraddr)
2279{
2280 int ret;
2281 struct ethtool_tunable tuna;
2282 const struct ethtool_ops *ops = dev->ethtool_ops;
2283 void *data;
2284
2285 if (!ops->set_tunable)
2286 return -EOPNOTSUPP;
2287 if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
2288 return -EFAULT;
2289 ret = ethtool_tunable_valid(&tuna);
2290 if (ret)
2291 return ret;
Govindarajulu Varadarajanf0db9b02014-09-03 03:17:20 +05302292 useraddr += sizeof(tuna);
Al Viro30e7e3e2017-05-13 18:31:26 -04002293 data = memdup_user(useraddr, tuna.len);
2294 if (IS_ERR(data))
2295 return PTR_ERR(data);
Govindarajulu Varadarajanf0db9b02014-09-03 03:17:20 +05302296 ret = ops->set_tunable(dev, &tuna, data);
2297
Govindarajulu Varadarajanf0db9b02014-09-03 03:17:20 +05302298 kfree(data);
2299 return ret;
2300}
2301
Arnd Bergmann3499e872019-03-07 16:58:35 +01002302static noinline_for_stack int
2303ethtool_get_per_queue_coalesce(struct net_device *dev,
2304 void __user *useraddr,
2305 struct ethtool_per_queue_op *per_queue_opt)
Kan Liang421797b2016-02-19 09:24:02 -05002306{
2307 u32 bit;
2308 int ret;
2309 DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
2310
2311 if (!dev->ethtool_ops->get_per_queue_coalesce)
2312 return -EOPNOTSUPP;
2313
2314 useraddr += sizeof(*per_queue_opt);
2315
Yury Norov3aa56882018-02-06 15:38:06 -08002316 bitmap_from_arr32(queue_mask, per_queue_opt->queue_mask,
2317 MAX_NUM_QUEUE);
Kan Liang421797b2016-02-19 09:24:02 -05002318
2319 for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
2320 struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
2321
2322 ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, &coalesce);
2323 if (ret != 0)
2324 return ret;
2325 if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
2326 return -EFAULT;
2327 useraddr += sizeof(coalesce);
2328 }
2329
2330 return 0;
2331}
2332
Arnd Bergmann3499e872019-03-07 16:58:35 +01002333static noinline_for_stack int
2334ethtool_set_per_queue_coalesce(struct net_device *dev,
2335 void __user *useraddr,
2336 struct ethtool_per_queue_op *per_queue_opt)
Kan Liangf38d1382016-02-19 09:24:03 -05002337{
2338 u32 bit;
2339 int i, ret = 0;
2340 int n_queue;
2341 struct ethtool_coalesce *backup = NULL, *tmp = NULL;
2342 DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
2343
2344 if ((!dev->ethtool_ops->set_per_queue_coalesce) ||
2345 (!dev->ethtool_ops->get_per_queue_coalesce))
2346 return -EOPNOTSUPP;
2347
2348 useraddr += sizeof(*per_queue_opt);
2349
Yury Norov3aa56882018-02-06 15:38:06 -08002350 bitmap_from_arr32(queue_mask, per_queue_opt->queue_mask, MAX_NUM_QUEUE);
Kan Liangf38d1382016-02-19 09:24:03 -05002351 n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE);
2352 tmp = backup = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL);
2353 if (!backup)
2354 return -ENOMEM;
2355
2356 for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
2357 struct ethtool_coalesce coalesce;
2358
2359 ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, tmp);
2360 if (ret != 0)
2361 goto roll_back;
2362
2363 tmp++;
2364
2365 if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) {
2366 ret = -EFAULT;
2367 goto roll_back;
2368 }
2369
Jakub Kicinski95cddcb2020-03-04 21:15:31 -08002370 if (!ethtool_set_coalesce_supported(dev, &coalesce)) {
2371 ret = -EOPNOTSUPP;
2372 goto roll_back;
2373 }
2374
Kan Liangf38d1382016-02-19 09:24:03 -05002375 ret = dev->ethtool_ops->set_per_queue_coalesce(dev, bit, &coalesce);
2376 if (ret != 0)
2377 goto roll_back;
2378
2379 useraddr += sizeof(coalesce);
2380 }
2381
2382roll_back:
2383 if (ret != 0) {
2384 tmp = backup;
2385 for_each_set_bit(i, queue_mask, bit) {
2386 dev->ethtool_ops->set_per_queue_coalesce(dev, i, tmp);
2387 tmp++;
2388 }
2389 }
2390 kfree(backup);
2391
2392 return ret;
2393}
2394
Arnd Bergmann3499e872019-03-07 16:58:35 +01002395static int noinline_for_stack ethtool_set_per_queue(struct net_device *dev,
Wenwen Wang58f5bbe2018-10-08 10:49:35 -05002396 void __user *useraddr, u32 sub_cmd)
Kan Liangac2c7ad2016-02-19 09:24:01 -05002397{
2398 struct ethtool_per_queue_op per_queue_opt;
2399
2400 if (copy_from_user(&per_queue_opt, useraddr, sizeof(per_queue_opt)))
2401 return -EFAULT;
2402
Wenwen Wang58f5bbe2018-10-08 10:49:35 -05002403 if (per_queue_opt.sub_command != sub_cmd)
2404 return -EINVAL;
2405
Kan Liangac2c7ad2016-02-19 09:24:01 -05002406 switch (per_queue_opt.sub_command) {
Kan Liang421797b2016-02-19 09:24:02 -05002407 case ETHTOOL_GCOALESCE:
2408 return ethtool_get_per_queue_coalesce(dev, useraddr, &per_queue_opt);
Kan Liangf38d1382016-02-19 09:24:03 -05002409 case ETHTOOL_SCOALESCE:
2410 return ethtool_set_per_queue_coalesce(dev, useraddr, &per_queue_opt);
Kan Liangac2c7ad2016-02-19 09:24:01 -05002411 default:
2412 return -EOPNOTSUPP;
2413 };
2414}
2415
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002416static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna)
2417{
2418 switch (tuna->id) {
Raju Lakkaraju65feddd2016-11-17 13:07:23 +01002419 case ETHTOOL_PHY_DOWNSHIFT:
Heiner Kallweit3aeb0802019-03-25 19:34:58 +01002420 case ETHTOOL_PHY_FAST_LINK_DOWN:
Raju Lakkaraju65feddd2016-11-17 13:07:23 +01002421 if (tuna->len != sizeof(u8) ||
2422 tuna->type_id != ETHTOOL_TUNABLE_U8)
2423 return -EINVAL;
2424 break;
Alexandru Ardelean9f2f13f2019-09-16 10:35:25 +03002425 case ETHTOOL_PHY_EDPD:
2426 if (tuna->len != sizeof(u16) ||
2427 tuna->type_id != ETHTOOL_TUNABLE_U16)
2428 return -EINVAL;
2429 break;
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002430 default:
2431 return -EINVAL;
2432 }
2433
2434 return 0;
2435}
2436
2437static int get_phy_tunable(struct net_device *dev, void __user *useraddr)
2438{
2439 int ret;
2440 struct ethtool_tunable tuna;
2441 struct phy_device *phydev = dev->phydev;
2442 void *data;
2443
2444 if (!(phydev && phydev->drv && phydev->drv->get_tunable))
2445 return -EOPNOTSUPP;
2446
2447 if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
2448 return -EFAULT;
2449 ret = ethtool_phy_tunable_valid(&tuna);
2450 if (ret)
2451 return ret;
2452 data = kmalloc(tuna.len, GFP_USER);
2453 if (!data)
2454 return -ENOMEM;
Florian Fainelli4b652462016-11-22 13:55:31 -08002455 mutex_lock(&phydev->lock);
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002456 ret = phydev->drv->get_tunable(phydev, &tuna, data);
Florian Fainelli4b652462016-11-22 13:55:31 -08002457 mutex_unlock(&phydev->lock);
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002458 if (ret)
2459 goto out;
2460 useraddr += sizeof(tuna);
2461 ret = -EFAULT;
2462 if (copy_to_user(useraddr, data, tuna.len))
2463 goto out;
2464 ret = 0;
2465
2466out:
2467 kfree(data);
2468 return ret;
2469}
2470
2471static int set_phy_tunable(struct net_device *dev, void __user *useraddr)
2472{
2473 int ret;
2474 struct ethtool_tunable tuna;
2475 struct phy_device *phydev = dev->phydev;
2476 void *data;
2477
2478 if (!(phydev && phydev->drv && phydev->drv->set_tunable))
2479 return -EOPNOTSUPP;
2480 if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
2481 return -EFAULT;
2482 ret = ethtool_phy_tunable_valid(&tuna);
2483 if (ret)
2484 return ret;
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002485 useraddr += sizeof(tuna);
Al Viro30e7e3e2017-05-13 18:31:26 -04002486 data = memdup_user(useraddr, tuna.len);
2487 if (IS_ERR(data))
2488 return PTR_ERR(data);
Florian Fainelli4b652462016-11-22 13:55:31 -08002489 mutex_lock(&phydev->lock);
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002490 ret = phydev->drv->set_tunable(phydev, &tuna, data);
Florian Fainelli4b652462016-11-22 13:55:31 -08002491 mutex_unlock(&phydev->lock);
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002492
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002493 kfree(data);
2494 return ret;
2495}
2496
Vidya Sagar Ravipati1a5f3da2017-07-27 16:47:26 -07002497static int ethtool_get_fecparam(struct net_device *dev, void __user *useraddr)
2498{
Li RongQinge83887f2019-02-27 20:47:57 +08002499 struct ethtool_fecparam fecparam = { .cmd = ETHTOOL_GFECPARAM };
Edward Creea6d50512018-02-28 19:15:58 +00002500 int rc;
Vidya Sagar Ravipati1a5f3da2017-07-27 16:47:26 -07002501
2502 if (!dev->ethtool_ops->get_fecparam)
2503 return -EOPNOTSUPP;
2504
Edward Creea6d50512018-02-28 19:15:58 +00002505 rc = dev->ethtool_ops->get_fecparam(dev, &fecparam);
2506 if (rc)
2507 return rc;
Vidya Sagar Ravipati1a5f3da2017-07-27 16:47:26 -07002508
2509 if (copy_to_user(useraddr, &fecparam, sizeof(fecparam)))
2510 return -EFAULT;
2511 return 0;
2512}
2513
2514static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr)
2515{
2516 struct ethtool_fecparam fecparam;
2517
2518 if (!dev->ethtool_ops->set_fecparam)
2519 return -EOPNOTSUPP;
2520
2521 if (copy_from_user(&fecparam, useraddr, sizeof(fecparam)))
2522 return -EFAULT;
2523
2524 return dev->ethtool_ops->set_fecparam(dev, &fecparam);
2525}
2526
Yan Burman600fed52013-06-03 02:03:34 +00002527/* The main entry point in this file. Called from net/core/dev_ioctl.c */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528
Eric W. Biederman881d9662007-09-17 11:56:21 -07002529int dev_ethtool(struct net *net, struct ifreq *ifr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530{
Eric W. Biederman881d9662007-09-17 11:56:21 -07002531 struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532 void __user *useraddr = ifr->ifr_data;
Kan Liangac2c7ad2016-02-19 09:24:01 -05002533 u32 ethcmd, sub_cmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 int rc;
Bjørn Morkb29d3142013-05-01 23:06:42 +00002535 netdev_features_t old_features;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537 if (!dev || !netif_device_present(dev))
2538 return -ENODEV;
2539
chavey97f8aef2010-04-07 21:54:42 -07002540 if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541 return -EFAULT;
2542
Kan Liangac2c7ad2016-02-19 09:24:01 -05002543 if (ethcmd == ETHTOOL_PERQUEUE) {
2544 if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd)))
2545 return -EFAULT;
2546 } else {
2547 sub_cmd = ethcmd;
2548 }
Stephen Hemminger75f31232006-09-28 15:13:37 -07002549 /* Allow some commands to be done by anyone */
Kan Liangac2c7ad2016-02-19 09:24:01 -05002550 switch (sub_cmd) {
stephen hemminger0fdc1002010-08-23 10:24:18 +00002551 case ETHTOOL_GSET:
Stephen Hemminger75f31232006-09-28 15:13:37 -07002552 case ETHTOOL_GDRVINFO:
Stephen Hemminger75f31232006-09-28 15:13:37 -07002553 case ETHTOOL_GMSGLVL:
Ben Hutchings2da45db2012-06-12 13:05:41 +00002554 case ETHTOOL_GLINK:
Stephen Hemminger75f31232006-09-28 15:13:37 -07002555 case ETHTOOL_GCOALESCE:
2556 case ETHTOOL_GRINGPARAM:
2557 case ETHTOOL_GPAUSEPARAM:
2558 case ETHTOOL_GRXCSUM:
2559 case ETHTOOL_GTXCSUM:
2560 case ETHTOOL_GSG:
Michał Mirosławf80400a2012-01-22 00:20:40 +00002561 case ETHTOOL_GSSET_INFO:
Stephen Hemminger75f31232006-09-28 15:13:37 -07002562 case ETHTOOL_GSTRINGS:
Ben Hutchings2da45db2012-06-12 13:05:41 +00002563 case ETHTOOL_GSTATS:
Andrew Lunnf3a40942015-12-30 16:28:25 +01002564 case ETHTOOL_GPHYSTATS:
Stephen Hemminger75f31232006-09-28 15:13:37 -07002565 case ETHTOOL_GTSO:
2566 case ETHTOOL_GPERMADDR:
Maciej Żenczykowski474ff262018-09-22 01:34:01 -07002567 case ETHTOOL_GUFO:
Stephen Hemminger75f31232006-09-28 15:13:37 -07002568 case ETHTOOL_GGSO:
stephen hemminger1cab8192010-02-11 13:48:29 +00002569 case ETHTOOL_GGRO:
Jeff Garzik339bf022007-08-15 16:01:32 -07002570 case ETHTOOL_GFLAGS:
2571 case ETHTOOL_GPFLAGS:
Santwona Behera0853ad62008-07-02 03:47:41 -07002572 case ETHTOOL_GRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08002573 case ETHTOOL_GRXRINGS:
2574 case ETHTOOL_GRXCLSRLCNT:
2575 case ETHTOOL_GRXCLSRULE:
2576 case ETHTOOL_GRXCLSRLALL:
Ben Hutchings2da45db2012-06-12 13:05:41 +00002577 case ETHTOOL_GRXFHINDIR:
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05302578 case ETHTOOL_GRSSH:
Michał Mirosław5455c692011-02-15 16:59:17 +00002579 case ETHTOOL_GFEATURES:
Ben Hutchings2da45db2012-06-12 13:05:41 +00002580 case ETHTOOL_GCHANNELS:
Richard Cochranc8f3a8c2012-04-03 22:59:17 +00002581 case ETHTOOL_GET_TS_INFO:
Ben Hutchings2da45db2012-06-12 13:05:41 +00002582 case ETHTOOL_GEEE:
Govindarajulu Varadarajanf0db9b02014-09-03 03:17:20 +05302583 case ETHTOOL_GTUNABLE:
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002584 case ETHTOOL_PHY_GTUNABLE:
Miroslav Lichvar8006f6b2016-11-24 10:55:06 +01002585 case ETHTOOL_GLINKSETTINGS:
Vidya Sagar Ravipati1a5f3da2017-07-27 16:47:26 -07002586 case ETHTOOL_GFECPARAM:
Stephen Hemminger75f31232006-09-28 15:13:37 -07002587 break;
2588 default:
Eric W. Biederman5e1fccc2012-11-16 03:03:04 +00002589 if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
Stephen Hemminger75f31232006-09-28 15:13:37 -07002590 return -EPERM;
2591 }
2592
chavey97f8aef2010-04-07 21:54:42 -07002593 if (dev->ethtool_ops->begin) {
2594 rc = dev->ethtool_ops->begin(dev);
2595 if (rc < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596 return rc;
chavey97f8aef2010-04-07 21:54:42 -07002597 }
Stephen Hemmingerd8a33ac2005-05-29 14:13:47 -07002598 old_features = dev->features;
2599
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 switch (ethcmd) {
2601 case ETHTOOL_GSET:
2602 rc = ethtool_get_settings(dev, useraddr);
2603 break;
2604 case ETHTOOL_SSET:
2605 rc = ethtool_set_settings(dev, useraddr);
2606 break;
2607 case ETHTOOL_GDRVINFO:
2608 rc = ethtool_get_drvinfo(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609 break;
2610 case ETHTOOL_GREGS:
2611 rc = ethtool_get_regs(dev, useraddr);
2612 break;
2613 case ETHTOOL_GWOL:
2614 rc = ethtool_get_wol(dev, useraddr);
2615 break;
2616 case ETHTOOL_SWOL:
2617 rc = ethtool_set_wol(dev, useraddr);
2618 break;
2619 case ETHTOOL_GMSGLVL:
Jeff Garzik13c99b22007-08-15 16:01:56 -07002620 rc = ethtool_get_value(dev, useraddr, ethcmd,
2621 dev->ethtool_ops->get_msglevel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 break;
2623 case ETHTOOL_SMSGLVL:
Jeff Garzik13c99b22007-08-15 16:01:56 -07002624 rc = ethtool_set_value_void(dev, useraddr,
2625 dev->ethtool_ops->set_msglevel);
Michal Kubecek0bda7af2020-01-26 23:11:10 +01002626 if (!rc)
2627 ethtool_notify(dev, ETHTOOL_MSG_DEBUG_NTF, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628 break;
Yuval Mintz80f12ec2012-06-06 17:13:06 +00002629 case ETHTOOL_GEEE:
2630 rc = ethtool_get_eee(dev, useraddr);
2631 break;
2632 case ETHTOOL_SEEE:
2633 rc = ethtool_set_eee(dev, useraddr);
2634 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635 case ETHTOOL_NWAY_RST:
2636 rc = ethtool_nway_reset(dev);
2637 break;
2638 case ETHTOOL_GLINK:
Ben Hutchingse596e6e2010-12-09 12:08:35 +00002639 rc = ethtool_get_link(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640 break;
2641 case ETHTOOL_GEEPROM:
2642 rc = ethtool_get_eeprom(dev, useraddr);
2643 break;
2644 case ETHTOOL_SEEPROM:
2645 rc = ethtool_set_eeprom(dev, useraddr);
2646 break;
2647 case ETHTOOL_GCOALESCE:
2648 rc = ethtool_get_coalesce(dev, useraddr);
2649 break;
2650 case ETHTOOL_SCOALESCE:
2651 rc = ethtool_set_coalesce(dev, useraddr);
2652 break;
2653 case ETHTOOL_GRINGPARAM:
2654 rc = ethtool_get_ringparam(dev, useraddr);
2655 break;
2656 case ETHTOOL_SRINGPARAM:
2657 rc = ethtool_set_ringparam(dev, useraddr);
2658 break;
2659 case ETHTOOL_GPAUSEPARAM:
2660 rc = ethtool_get_pauseparam(dev, useraddr);
2661 break;
2662 case ETHTOOL_SPAUSEPARAM:
2663 rc = ethtool_set_pauseparam(dev, useraddr);
2664 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665 case ETHTOOL_TEST:
2666 rc = ethtool_self_test(dev, useraddr);
2667 break;
2668 case ETHTOOL_GSTRINGS:
2669 rc = ethtool_get_strings(dev, useraddr);
2670 break;
2671 case ETHTOOL_PHYS_ID:
2672 rc = ethtool_phys_id(dev, useraddr);
2673 break;
2674 case ETHTOOL_GSTATS:
2675 rc = ethtool_get_stats(dev, useraddr);
2676 break;
Jon Wetzela6f9a702005-08-20 17:15:54 -07002677 case ETHTOOL_GPERMADDR:
2678 rc = ethtool_get_perm_addr(dev, useraddr);
2679 break;
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07002680 case ETHTOOL_GFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07002681 rc = ethtool_get_value(dev, useraddr, ethcmd,
Michał Mirosławbc5787c62011-11-15 15:29:55 +00002682 __ethtool_get_flags);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07002683 break;
2684 case ETHTOOL_SFLAGS:
Michał Mirosławda8ac86c2011-02-15 16:59:18 +00002685 rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07002686 break;
Jeff Garzik339bf022007-08-15 16:01:32 -07002687 case ETHTOOL_GPFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07002688 rc = ethtool_get_value(dev, useraddr, ethcmd,
2689 dev->ethtool_ops->get_priv_flags);
Michal Kubecek111dcba2020-03-12 21:08:18 +01002690 if (!rc)
2691 ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL);
Jeff Garzik339bf022007-08-15 16:01:32 -07002692 break;
2693 case ETHTOOL_SPFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07002694 rc = ethtool_set_value(dev, useraddr,
2695 dev->ethtool_ops->set_priv_flags);
Jeff Garzik339bf022007-08-15 16:01:32 -07002696 break;
Santwona Behera0853ad62008-07-02 03:47:41 -07002697 case ETHTOOL_GRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08002698 case ETHTOOL_GRXRINGS:
2699 case ETHTOOL_GRXCLSRLCNT:
2700 case ETHTOOL_GRXCLSRULE:
2701 case ETHTOOL_GRXCLSRLALL:
Ben Hutchingsbf988432010-06-28 08:45:58 +00002702 rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);
Santwona Behera0853ad62008-07-02 03:47:41 -07002703 break;
2704 case ETHTOOL_SRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08002705 case ETHTOOL_SRXCLSRLDEL:
2706 case ETHTOOL_SRXCLSRLINS:
Ben Hutchingsbf988432010-06-28 08:45:58 +00002707 rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
Santwona Behera0853ad62008-07-02 03:47:41 -07002708 break;
Ajit Khaparde05c6a8d2009-09-02 17:02:55 +00002709 case ETHTOOL_FLASHDEV:
2710 rc = ethtool_flash_device(dev, useraddr);
2711 break;
Ben Hutchingsd73d3a82009-10-05 10:59:58 +00002712 case ETHTOOL_RESET:
2713 rc = ethtool_reset(dev, useraddr);
2714 break;
Jeff Garzik723b2f52010-03-03 22:51:50 +00002715 case ETHTOOL_GSSET_INFO:
2716 rc = ethtool_get_sset_info(dev, useraddr);
2717 break;
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +00002718 case ETHTOOL_GRXFHINDIR:
2719 rc = ethtool_get_rxfh_indir(dev, useraddr);
2720 break;
2721 case ETHTOOL_SRXFHINDIR:
2722 rc = ethtool_set_rxfh_indir(dev, useraddr);
2723 break;
Venkata Duvvuru3de0b592014-04-21 15:37:59 +05302724 case ETHTOOL_GRSSH:
2725 rc = ethtool_get_rxfh(dev, useraddr);
2726 break;
2727 case ETHTOOL_SRSSH:
2728 rc = ethtool_set_rxfh(dev, useraddr);
2729 break;
Michał Mirosław5455c692011-02-15 16:59:17 +00002730 case ETHTOOL_GFEATURES:
2731 rc = ethtool_get_features(dev, useraddr);
2732 break;
2733 case ETHTOOL_SFEATURES:
2734 rc = ethtool_set_features(dev, useraddr);
2735 break;
Michał Mirosław0a417702011-02-15 16:59:17 +00002736 case ETHTOOL_GTXCSUM:
Michał Mirosławe83d3602011-02-15 16:59:18 +00002737 case ETHTOOL_GRXCSUM:
Michał Mirosław0a417702011-02-15 16:59:17 +00002738 case ETHTOOL_GSG:
2739 case ETHTOOL_GTSO:
Michał Mirosław0a417702011-02-15 16:59:17 +00002740 case ETHTOOL_GGSO:
2741 case ETHTOOL_GGRO:
2742 rc = ethtool_get_one_feature(dev, useraddr, ethcmd);
2743 break;
2744 case ETHTOOL_STXCSUM:
Michał Mirosławe83d3602011-02-15 16:59:18 +00002745 case ETHTOOL_SRXCSUM:
Michał Mirosław0a417702011-02-15 16:59:17 +00002746 case ETHTOOL_SSG:
2747 case ETHTOOL_STSO:
Michał Mirosław0a417702011-02-15 16:59:17 +00002748 case ETHTOOL_SGSO:
2749 case ETHTOOL_SGRO:
2750 rc = ethtool_set_one_feature(dev, useraddr, ethcmd);
2751 break;
amit salecha8b5933c2011-04-07 01:58:42 +00002752 case ETHTOOL_GCHANNELS:
2753 rc = ethtool_get_channels(dev, useraddr);
2754 break;
2755 case ETHTOOL_SCHANNELS:
2756 rc = ethtool_set_channels(dev, useraddr);
2757 break;
Anirban Chakraborty29dd54b2011-05-12 12:48:32 +00002758 case ETHTOOL_SET_DUMP:
2759 rc = ethtool_set_dump(dev, useraddr);
2760 break;
2761 case ETHTOOL_GET_DUMP_FLAG:
2762 rc = ethtool_get_dump_flag(dev, useraddr);
2763 break;
2764 case ETHTOOL_GET_DUMP_DATA:
2765 rc = ethtool_get_dump_data(dev, useraddr);
2766 break;
Richard Cochranc8f3a8c2012-04-03 22:59:17 +00002767 case ETHTOOL_GET_TS_INFO:
2768 rc = ethtool_get_ts_info(dev, useraddr);
2769 break;
Stuart Hodgson41c3cb62012-04-19 09:44:42 +01002770 case ETHTOOL_GMODULEINFO:
2771 rc = ethtool_get_module_info(dev, useraddr);
2772 break;
2773 case ETHTOOL_GMODULEEEPROM:
2774 rc = ethtool_get_module_eeprom(dev, useraddr);
2775 break;
Govindarajulu Varadarajanf0db9b02014-09-03 03:17:20 +05302776 case ETHTOOL_GTUNABLE:
2777 rc = ethtool_get_tunable(dev, useraddr);
2778 break;
2779 case ETHTOOL_STUNABLE:
2780 rc = ethtool_set_tunable(dev, useraddr);
2781 break;
Andrew Lunnf3a40942015-12-30 16:28:25 +01002782 case ETHTOOL_GPHYSTATS:
2783 rc = ethtool_get_phy_stats(dev, useraddr);
2784 break;
Kan Liangac2c7ad2016-02-19 09:24:01 -05002785 case ETHTOOL_PERQUEUE:
Wenwen Wang58f5bbe2018-10-08 10:49:35 -05002786 rc = ethtool_set_per_queue(dev, useraddr, sub_cmd);
Kan Liangac2c7ad2016-02-19 09:24:01 -05002787 break;
David Decotigny3f1ac7a2016-02-24 10:57:59 -08002788 case ETHTOOL_GLINKSETTINGS:
2789 rc = ethtool_get_link_ksettings(dev, useraddr);
2790 break;
2791 case ETHTOOL_SLINKSETTINGS:
2792 rc = ethtool_set_link_ksettings(dev, useraddr);
2793 break;
Raju Lakkaraju968ad9d2016-11-17 13:07:21 +01002794 case ETHTOOL_PHY_GTUNABLE:
2795 rc = get_phy_tunable(dev, useraddr);
2796 break;
2797 case ETHTOOL_PHY_STUNABLE:
2798 rc = set_phy_tunable(dev, useraddr);
2799 break;
Vidya Sagar Ravipati1a5f3da2017-07-27 16:47:26 -07002800 case ETHTOOL_GFECPARAM:
2801 rc = ethtool_get_fecparam(dev, useraddr);
2802 break;
2803 case ETHTOOL_SFECPARAM:
2804 rc = ethtool_set_fecparam(dev, useraddr);
2805 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 default:
Matthew Wilcox61a44b92007-07-31 14:00:02 -07002807 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 }
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09002809
Stephen Hemmingere71a4782007-04-10 20:10:33 -07002810 if (dev->ethtool_ops->complete)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 dev->ethtool_ops->complete(dev);
Stephen Hemmingerd8a33ac2005-05-29 14:13:47 -07002812
2813 if (old_features != dev->features)
2814 netdev_features_change(dev);
2815
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817}
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01002818
2819struct ethtool_rx_flow_key {
2820 struct flow_dissector_key_basic basic;
2821 union {
2822 struct flow_dissector_key_ipv4_addrs ipv4;
2823 struct flow_dissector_key_ipv6_addrs ipv6;
2824 };
2825 struct flow_dissector_key_ports tp;
2826 struct flow_dissector_key_ip ip;
2827 struct flow_dissector_key_vlan vlan;
2828 struct flow_dissector_key_eth_addrs eth_addrs;
2829} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
2830
2831struct ethtool_rx_flow_match {
2832 struct flow_dissector dissector;
2833 struct ethtool_rx_flow_key key;
2834 struct ethtool_rx_flow_key mask;
2835};
2836
2837struct ethtool_rx_flow_rule *
2838ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input)
2839{
2840 const struct ethtool_rx_flow_spec *fs = input->fs;
2841 static struct in6_addr zero_addr = {};
2842 struct ethtool_rx_flow_match *match;
2843 struct ethtool_rx_flow_rule *flow;
2844 struct flow_action_entry *act;
2845
2846 flow = kzalloc(sizeof(struct ethtool_rx_flow_rule) +
2847 sizeof(struct ethtool_rx_flow_match), GFP_KERNEL);
2848 if (!flow)
2849 return ERR_PTR(-ENOMEM);
2850
2851 /* ethtool_rx supports only one single action per rule. */
2852 flow->rule = flow_rule_alloc(1);
2853 if (!flow->rule) {
2854 kfree(flow);
2855 return ERR_PTR(-ENOMEM);
2856 }
2857
2858 match = (struct ethtool_rx_flow_match *)flow->priv;
2859 flow->rule->match.dissector = &match->dissector;
2860 flow->rule->match.mask = &match->mask;
2861 flow->rule->match.key = &match->key;
2862
2863 match->mask.basic.n_proto = htons(0xffff);
2864
2865 switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
Maxime Chevallier5b9469a2019-06-27 10:52:26 +02002866 case ETHER_FLOW: {
2867 const struct ethhdr *ether_spec, *ether_m_spec;
2868
2869 ether_spec = &fs->h_u.ether_spec;
2870 ether_m_spec = &fs->m_u.ether_spec;
2871
2872 if (!is_zero_ether_addr(ether_m_spec->h_source)) {
2873 ether_addr_copy(match->key.eth_addrs.src,
2874 ether_spec->h_source);
2875 ether_addr_copy(match->mask.eth_addrs.src,
2876 ether_m_spec->h_source);
2877 }
2878 if (!is_zero_ether_addr(ether_m_spec->h_dest)) {
2879 ether_addr_copy(match->key.eth_addrs.dst,
2880 ether_spec->h_dest);
2881 ether_addr_copy(match->mask.eth_addrs.dst,
2882 ether_m_spec->h_dest);
2883 }
2884 if (ether_m_spec->h_proto) {
2885 match->key.basic.n_proto = ether_spec->h_proto;
2886 match->mask.basic.n_proto = ether_m_spec->h_proto;
2887 }
2888 }
2889 break;
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01002890 case TCP_V4_FLOW:
2891 case UDP_V4_FLOW: {
2892 const struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec;
2893
2894 match->key.basic.n_proto = htons(ETH_P_IP);
2895
2896 v4_spec = &fs->h_u.tcp_ip4_spec;
2897 v4_m_spec = &fs->m_u.tcp_ip4_spec;
2898
2899 if (v4_m_spec->ip4src) {
2900 match->key.ipv4.src = v4_spec->ip4src;
2901 match->mask.ipv4.src = v4_m_spec->ip4src;
2902 }
2903 if (v4_m_spec->ip4dst) {
2904 match->key.ipv4.dst = v4_spec->ip4dst;
2905 match->mask.ipv4.dst = v4_m_spec->ip4dst;
2906 }
2907 if (v4_m_spec->ip4src ||
2908 v4_m_spec->ip4dst) {
2909 match->dissector.used_keys |=
2910 BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
2911 match->dissector.offset[FLOW_DISSECTOR_KEY_IPV4_ADDRS] =
2912 offsetof(struct ethtool_rx_flow_key, ipv4);
2913 }
2914 if (v4_m_spec->psrc) {
2915 match->key.tp.src = v4_spec->psrc;
2916 match->mask.tp.src = v4_m_spec->psrc;
2917 }
2918 if (v4_m_spec->pdst) {
2919 match->key.tp.dst = v4_spec->pdst;
2920 match->mask.tp.dst = v4_m_spec->pdst;
2921 }
2922 if (v4_m_spec->psrc ||
2923 v4_m_spec->pdst) {
2924 match->dissector.used_keys |=
2925 BIT(FLOW_DISSECTOR_KEY_PORTS);
2926 match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
2927 offsetof(struct ethtool_rx_flow_key, tp);
2928 }
2929 if (v4_m_spec->tos) {
2930 match->key.ip.tos = v4_spec->tos;
2931 match->mask.ip.tos = v4_m_spec->tos;
2932 match->dissector.used_keys |=
2933 BIT(FLOW_DISSECTOR_KEY_IP);
2934 match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
2935 offsetof(struct ethtool_rx_flow_key, ip);
2936 }
2937 }
2938 break;
2939 case TCP_V6_FLOW:
2940 case UDP_V6_FLOW: {
2941 const struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec;
2942
2943 match->key.basic.n_proto = htons(ETH_P_IPV6);
2944
2945 v6_spec = &fs->h_u.tcp_ip6_spec;
2946 v6_m_spec = &fs->m_u.tcp_ip6_spec;
2947 if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
2948 memcpy(&match->key.ipv6.src, v6_spec->ip6src,
2949 sizeof(match->key.ipv6.src));
2950 memcpy(&match->mask.ipv6.src, v6_m_spec->ip6src,
2951 sizeof(match->mask.ipv6.src));
2952 }
2953 if (memcmp(v6_m_spec->ip6dst, &zero_addr, sizeof(zero_addr))) {
2954 memcpy(&match->key.ipv6.dst, v6_spec->ip6dst,
2955 sizeof(match->key.ipv6.dst));
2956 memcpy(&match->mask.ipv6.dst, v6_m_spec->ip6dst,
2957 sizeof(match->mask.ipv6.dst));
2958 }
2959 if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr)) ||
2960 memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
2961 match->dissector.used_keys |=
2962 BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
2963 match->dissector.offset[FLOW_DISSECTOR_KEY_IPV6_ADDRS] =
2964 offsetof(struct ethtool_rx_flow_key, ipv6);
2965 }
2966 if (v6_m_spec->psrc) {
2967 match->key.tp.src = v6_spec->psrc;
2968 match->mask.tp.src = v6_m_spec->psrc;
2969 }
2970 if (v6_m_spec->pdst) {
2971 match->key.tp.dst = v6_spec->pdst;
2972 match->mask.tp.dst = v6_m_spec->pdst;
2973 }
2974 if (v6_m_spec->psrc ||
2975 v6_m_spec->pdst) {
2976 match->dissector.used_keys |=
2977 BIT(FLOW_DISSECTOR_KEY_PORTS);
2978 match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
2979 offsetof(struct ethtool_rx_flow_key, tp);
2980 }
2981 if (v6_m_spec->tclass) {
2982 match->key.ip.tos = v6_spec->tclass;
2983 match->mask.ip.tos = v6_m_spec->tclass;
2984 match->dissector.used_keys |=
2985 BIT(FLOW_DISSECTOR_KEY_IP);
2986 match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
2987 offsetof(struct ethtool_rx_flow_key, ip);
2988 }
2989 }
2990 break;
2991 default:
2992 ethtool_rx_flow_rule_destroy(flow);
2993 return ERR_PTR(-EINVAL);
2994 }
2995
2996 switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
2997 case TCP_V4_FLOW:
2998 case TCP_V6_FLOW:
2999 match->key.basic.ip_proto = IPPROTO_TCP;
3000 break;
3001 case UDP_V4_FLOW:
3002 case UDP_V6_FLOW:
3003 match->key.basic.ip_proto = IPPROTO_UDP;
3004 break;
3005 }
3006 match->mask.basic.ip_proto = 0xff;
3007
3008 match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
3009 match->dissector.offset[FLOW_DISSECTOR_KEY_BASIC] =
3010 offsetof(struct ethtool_rx_flow_key, basic);
3011
3012 if (fs->flow_type & FLOW_EXT) {
3013 const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
3014 const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
3015
Maxime Chevallierb73484b2019-05-30 16:08:40 +02003016 if (ext_m_spec->vlan_etype) {
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01003017 match->key.vlan.vlan_tpid = ext_h_spec->vlan_etype;
3018 match->mask.vlan.vlan_tpid = ext_m_spec->vlan_etype;
Maxime Chevallierb73484b2019-05-30 16:08:40 +02003019 }
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01003020
Maxime Chevallierb73484b2019-05-30 16:08:40 +02003021 if (ext_m_spec->vlan_tci) {
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01003022 match->key.vlan.vlan_id =
3023 ntohs(ext_h_spec->vlan_tci) & 0x0fff;
3024 match->mask.vlan.vlan_id =
3025 ntohs(ext_m_spec->vlan_tci) & 0x0fff;
3026
Maxime Chevallierf0d2ca12019-06-12 17:18:38 +02003027 match->key.vlan.vlan_dei =
3028 !!(ext_h_spec->vlan_tci & htons(0x1000));
3029 match->mask.vlan.vlan_dei =
3030 !!(ext_m_spec->vlan_tci & htons(0x1000));
3031
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01003032 match->key.vlan.vlan_priority =
3033 (ntohs(ext_h_spec->vlan_tci) & 0xe000) >> 13;
3034 match->mask.vlan.vlan_priority =
3035 (ntohs(ext_m_spec->vlan_tci) & 0xe000) >> 13;
Maxime Chevallierb73484b2019-05-30 16:08:40 +02003036 }
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01003037
Maxime Chevallierb73484b2019-05-30 16:08:40 +02003038 if (ext_m_spec->vlan_etype ||
3039 ext_m_spec->vlan_tci) {
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01003040 match->dissector.used_keys |=
3041 BIT(FLOW_DISSECTOR_KEY_VLAN);
3042 match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] =
3043 offsetof(struct ethtool_rx_flow_key, vlan);
3044 }
3045 }
3046 if (fs->flow_type & FLOW_MAC_EXT) {
3047 const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
3048 const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
3049
Nathan Chancellor8b34ec62019-02-07 21:46:53 -07003050 memcpy(match->key.eth_addrs.dst, ext_h_spec->h_dest,
3051 ETH_ALEN);
3052 memcpy(match->mask.eth_addrs.dst, ext_m_spec->h_dest,
3053 ETH_ALEN);
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01003054
Nathan Chancellor8b34ec62019-02-07 21:46:53 -07003055 match->dissector.used_keys |=
3056 BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
3057 match->dissector.offset[FLOW_DISSECTOR_KEY_ETH_ADDRS] =
3058 offsetof(struct ethtool_rx_flow_key, eth_addrs);
Pablo Neira Ayusoeca42052019-02-02 12:50:51 +01003059 }
3060
3061 act = &flow->rule->action.entries[0];
3062 switch (fs->ring_cookie) {
3063 case RX_CLS_FLOW_DISC:
3064 act->id = FLOW_ACTION_DROP;
3065 break;
3066 case RX_CLS_FLOW_WAKE:
3067 act->id = FLOW_ACTION_WAKE;
3068 break;
3069 default:
3070 act->id = FLOW_ACTION_QUEUE;
3071 if (fs->flow_type & FLOW_RSS)
3072 act->queue.ctx = input->rss_ctx;
3073
3074 act->queue.vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
3075 act->queue.index = ethtool_get_flow_spec_ring(fs->ring_cookie);
3076 break;
3077 }
3078
3079 return flow;
3080}
3081EXPORT_SYMBOL(ethtool_rx_flow_rule_create);
3082
3083void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *flow)
3084{
3085 kfree(flow->rule);
3086 kfree(flow);
3087}
3088EXPORT_SYMBOL(ethtool_rx_flow_rule_destroy);