1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * net/core/ethtool.c - Ethtool ioctl handler
4*4882a593Smuzhiyun * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * This file is where we call all the ethtool_ops commands to get
7*4882a593Smuzhiyun * the information ethtool needs.
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/compat.h>
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/types.h>
13*4882a593Smuzhiyun #include <linux/capability.h>
14*4882a593Smuzhiyun #include <linux/errno.h>
15*4882a593Smuzhiyun #include <linux/ethtool.h>
16*4882a593Smuzhiyun #include <linux/netdevice.h>
17*4882a593Smuzhiyun #include <linux/net_tstamp.h>
18*4882a593Smuzhiyun #include <linux/phy.h>
19*4882a593Smuzhiyun #include <linux/bitops.h>
20*4882a593Smuzhiyun #include <linux/uaccess.h>
21*4882a593Smuzhiyun #include <linux/vmalloc.h>
22*4882a593Smuzhiyun #include <linux/sfp.h>
23*4882a593Smuzhiyun #include <linux/slab.h>
24*4882a593Smuzhiyun #include <linux/rtnetlink.h>
25*4882a593Smuzhiyun #include <linux/sched/signal.h>
26*4882a593Smuzhiyun #include <linux/net.h>
27*4882a593Smuzhiyun #include <net/devlink.h>
28*4882a593Smuzhiyun #include <net/xdp_sock_drv.h>
29*4882a593Smuzhiyun #include <net/flow_offload.h>
30*4882a593Smuzhiyun #include <linux/ethtool_netlink.h>
31*4882a593Smuzhiyun #include <generated/utsrelease.h>
32*4882a593Smuzhiyun #include "common.h"
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /*
35*4882a593Smuzhiyun * Some useful ethtool_ops methods that're device independent.
36*4882a593Smuzhiyun * If we find that all drivers want to do the same thing here,
37*4882a593Smuzhiyun * we can turn these into dev_() function calls.
38*4882a593Smuzhiyun */
39*4882a593Smuzhiyun
ethtool_op_get_link(struct net_device * dev)40*4882a593Smuzhiyun u32 ethtool_op_get_link(struct net_device *dev)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun return netif_carrier_ok(dev) ? 1 : 0;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun EXPORT_SYMBOL(ethtool_op_get_link);
45*4882a593Smuzhiyun
ethtool_op_get_ts_info(struct net_device * dev,struct ethtool_ts_info * info)46*4882a593Smuzhiyun int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun info->so_timestamping =
49*4882a593Smuzhiyun SOF_TIMESTAMPING_TX_SOFTWARE |
50*4882a593Smuzhiyun SOF_TIMESTAMPING_RX_SOFTWARE |
51*4882a593Smuzhiyun SOF_TIMESTAMPING_SOFTWARE;
52*4882a593Smuzhiyun info->phc_index = -1;
53*4882a593Smuzhiyun return 0;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun EXPORT_SYMBOL(ethtool_op_get_ts_info);
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun /* Handlers for each ethtool command */
58*4882a593Smuzhiyun
ethtool_get_features(struct net_device * dev,void __user * useraddr)59*4882a593Smuzhiyun static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun struct ethtool_gfeatures cmd = {
62*4882a593Smuzhiyun .cmd = ETHTOOL_GFEATURES,
63*4882a593Smuzhiyun .size = ETHTOOL_DEV_FEATURE_WORDS,
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
66*4882a593Smuzhiyun u32 __user *sizeaddr;
67*4882a593Smuzhiyun u32 copy_size;
68*4882a593Smuzhiyun int i;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun /* in case feature bits run out again */
71*4882a593Smuzhiyun BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) > sizeof(netdev_features_t));
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {
74*4882a593Smuzhiyun features[i].available = (u32)(dev->hw_features >> (32 * i));
75*4882a593Smuzhiyun features[i].requested = (u32)(dev->wanted_features >> (32 * i));
76*4882a593Smuzhiyun features[i].active = (u32)(dev->features >> (32 * i));
77*4882a593Smuzhiyun features[i].never_changed =
78*4882a593Smuzhiyun (u32)(NETIF_F_NEVER_CHANGE >> (32 * i));
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
82*4882a593Smuzhiyun if (get_user(copy_size, sizeaddr))
83*4882a593Smuzhiyun return -EFAULT;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun if (copy_size > ETHTOOL_DEV_FEATURE_WORDS)
86*4882a593Smuzhiyun copy_size = ETHTOOL_DEV_FEATURE_WORDS;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
89*4882a593Smuzhiyun return -EFAULT;
90*4882a593Smuzhiyun useraddr += sizeof(cmd);
91*4882a593Smuzhiyun if (copy_to_user(useraddr, features, copy_size * sizeof(*features)))
92*4882a593Smuzhiyun return -EFAULT;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return 0;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun
ethtool_set_features(struct net_device * dev,void __user * useraddr)97*4882a593Smuzhiyun static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun struct ethtool_sfeatures cmd;
100*4882a593Smuzhiyun struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
101*4882a593Smuzhiyun netdev_features_t wanted = 0, valid = 0;
102*4882a593Smuzhiyun int i, ret = 0;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
105*4882a593Smuzhiyun return -EFAULT;
106*4882a593Smuzhiyun useraddr += sizeof(cmd);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS)
109*4882a593Smuzhiyun return -EINVAL;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (copy_from_user(features, useraddr, sizeof(features)))
112*4882a593Smuzhiyun return -EFAULT;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {
115*4882a593Smuzhiyun valid |= (netdev_features_t)features[i].valid << (32 * i);
116*4882a593Smuzhiyun wanted |= (netdev_features_t)features[i].requested << (32 * i);
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun if (valid & ~NETIF_F_ETHTOOL_BITS)
120*4882a593Smuzhiyun return -EINVAL;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun if (valid & ~dev->hw_features) {
123*4882a593Smuzhiyun valid &= dev->hw_features;
124*4882a593Smuzhiyun ret |= ETHTOOL_F_UNSUPPORTED;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun dev->wanted_features &= ~valid;
128*4882a593Smuzhiyun dev->wanted_features |= wanted & valid;
129*4882a593Smuzhiyun __netdev_update_features(dev);
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun if ((dev->wanted_features ^ dev->features) & valid)
132*4882a593Smuzhiyun ret |= ETHTOOL_F_WISH;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun return ret;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
__ethtool_get_sset_count(struct net_device * dev,int sset)137*4882a593Smuzhiyun static int __ethtool_get_sset_count(struct net_device *dev, int sset)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
140*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun if (sset == ETH_SS_FEATURES)
143*4882a593Smuzhiyun return ARRAY_SIZE(netdev_features_strings);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun if (sset == ETH_SS_RSS_HASH_FUNCS)
146*4882a593Smuzhiyun return ARRAY_SIZE(rss_hash_func_strings);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (sset == ETH_SS_TUNABLES)
149*4882a593Smuzhiyun return ARRAY_SIZE(tunable_strings);
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun if (sset == ETH_SS_PHY_TUNABLES)
152*4882a593Smuzhiyun return ARRAY_SIZE(phy_tunable_strings);
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun if (sset == ETH_SS_PHY_STATS && dev->phydev &&
155*4882a593Smuzhiyun !ops->get_ethtool_phy_stats &&
156*4882a593Smuzhiyun phy_ops && phy_ops->get_sset_count)
157*4882a593Smuzhiyun return phy_ops->get_sset_count(dev->phydev);
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun if (sset == ETH_SS_LINK_MODES)
160*4882a593Smuzhiyun return __ETHTOOL_LINK_MODE_MASK_NBITS;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun if (ops->get_sset_count && ops->get_strings)
163*4882a593Smuzhiyun return ops->get_sset_count(dev, sset);
164*4882a593Smuzhiyun else
165*4882a593Smuzhiyun return -EOPNOTSUPP;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
__ethtool_get_strings(struct net_device * dev,u32 stringset,u8 * data)168*4882a593Smuzhiyun static void __ethtool_get_strings(struct net_device *dev,
169*4882a593Smuzhiyun u32 stringset, u8 *data)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
172*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun if (stringset == ETH_SS_FEATURES)
175*4882a593Smuzhiyun memcpy(data, netdev_features_strings,
176*4882a593Smuzhiyun sizeof(netdev_features_strings));
177*4882a593Smuzhiyun else if (stringset == ETH_SS_RSS_HASH_FUNCS)
178*4882a593Smuzhiyun memcpy(data, rss_hash_func_strings,
179*4882a593Smuzhiyun sizeof(rss_hash_func_strings));
180*4882a593Smuzhiyun else if (stringset == ETH_SS_TUNABLES)
181*4882a593Smuzhiyun memcpy(data, tunable_strings, sizeof(tunable_strings));
182*4882a593Smuzhiyun else if (stringset == ETH_SS_PHY_TUNABLES)
183*4882a593Smuzhiyun memcpy(data, phy_tunable_strings, sizeof(phy_tunable_strings));
184*4882a593Smuzhiyun else if (stringset == ETH_SS_PHY_STATS && dev->phydev &&
185*4882a593Smuzhiyun !ops->get_ethtool_phy_stats && phy_ops &&
186*4882a593Smuzhiyun phy_ops->get_strings)
187*4882a593Smuzhiyun phy_ops->get_strings(dev->phydev, data);
188*4882a593Smuzhiyun else if (stringset == ETH_SS_LINK_MODES)
189*4882a593Smuzhiyun memcpy(data, link_mode_names,
190*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS * ETH_GSTRING_LEN);
191*4882a593Smuzhiyun else
192*4882a593Smuzhiyun /* ops->get_strings is valid because checked earlier */
193*4882a593Smuzhiyun ops->get_strings(dev, stringset, data);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
ethtool_get_feature_mask(u32 eth_cmd)196*4882a593Smuzhiyun static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun /* feature masks of legacy discrete ethtool ops */
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun switch (eth_cmd) {
201*4882a593Smuzhiyun case ETHTOOL_GTXCSUM:
202*4882a593Smuzhiyun case ETHTOOL_STXCSUM:
203*4882a593Smuzhiyun return NETIF_F_CSUM_MASK | NETIF_F_FCOE_CRC |
204*4882a593Smuzhiyun NETIF_F_SCTP_CRC;
205*4882a593Smuzhiyun case ETHTOOL_GRXCSUM:
206*4882a593Smuzhiyun case ETHTOOL_SRXCSUM:
207*4882a593Smuzhiyun return NETIF_F_RXCSUM;
208*4882a593Smuzhiyun case ETHTOOL_GSG:
209*4882a593Smuzhiyun case ETHTOOL_SSG:
210*4882a593Smuzhiyun return NETIF_F_SG | NETIF_F_FRAGLIST;
211*4882a593Smuzhiyun case ETHTOOL_GTSO:
212*4882a593Smuzhiyun case ETHTOOL_STSO:
213*4882a593Smuzhiyun return NETIF_F_ALL_TSO;
214*4882a593Smuzhiyun case ETHTOOL_GGSO:
215*4882a593Smuzhiyun case ETHTOOL_SGSO:
216*4882a593Smuzhiyun return NETIF_F_GSO;
217*4882a593Smuzhiyun case ETHTOOL_GGRO:
218*4882a593Smuzhiyun case ETHTOOL_SGRO:
219*4882a593Smuzhiyun return NETIF_F_GRO;
220*4882a593Smuzhiyun default:
221*4882a593Smuzhiyun BUG();
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
ethtool_get_one_feature(struct net_device * dev,char __user * useraddr,u32 ethcmd)225*4882a593Smuzhiyun static int ethtool_get_one_feature(struct net_device *dev,
226*4882a593Smuzhiyun char __user *useraddr, u32 ethcmd)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun netdev_features_t mask = ethtool_get_feature_mask(ethcmd);
229*4882a593Smuzhiyun struct ethtool_value edata = {
230*4882a593Smuzhiyun .cmd = ethcmd,
231*4882a593Smuzhiyun .data = !!(dev->features & mask),
232*4882a593Smuzhiyun };
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun if (copy_to_user(useraddr, &edata, sizeof(edata)))
235*4882a593Smuzhiyun return -EFAULT;
236*4882a593Smuzhiyun return 0;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
ethtool_set_one_feature(struct net_device * dev,void __user * useraddr,u32 ethcmd)239*4882a593Smuzhiyun static int ethtool_set_one_feature(struct net_device *dev,
240*4882a593Smuzhiyun void __user *useraddr, u32 ethcmd)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun struct ethtool_value edata;
243*4882a593Smuzhiyun netdev_features_t mask;
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun if (copy_from_user(&edata, useraddr, sizeof(edata)))
246*4882a593Smuzhiyun return -EFAULT;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun mask = ethtool_get_feature_mask(ethcmd);
249*4882a593Smuzhiyun mask &= dev->hw_features;
250*4882a593Smuzhiyun if (!mask)
251*4882a593Smuzhiyun return -EOPNOTSUPP;
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun if (edata.data)
254*4882a593Smuzhiyun dev->wanted_features |= mask;
255*4882a593Smuzhiyun else
256*4882a593Smuzhiyun dev->wanted_features &= ~mask;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun __netdev_update_features(dev);
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun return 0;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun #define ETH_ALL_FLAGS (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | \
264*4882a593Smuzhiyun ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH)
265*4882a593Smuzhiyun #define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_CTAG_RX | \
266*4882a593Smuzhiyun NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_NTUPLE | \
267*4882a593Smuzhiyun NETIF_F_RXHASH)
268*4882a593Smuzhiyun
__ethtool_get_flags(struct net_device * dev)269*4882a593Smuzhiyun static u32 __ethtool_get_flags(struct net_device *dev)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun u32 flags = 0;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun if (dev->features & NETIF_F_LRO)
274*4882a593Smuzhiyun flags |= ETH_FLAG_LRO;
275*4882a593Smuzhiyun if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
276*4882a593Smuzhiyun flags |= ETH_FLAG_RXVLAN;
277*4882a593Smuzhiyun if (dev->features & NETIF_F_HW_VLAN_CTAG_TX)
278*4882a593Smuzhiyun flags |= ETH_FLAG_TXVLAN;
279*4882a593Smuzhiyun if (dev->features & NETIF_F_NTUPLE)
280*4882a593Smuzhiyun flags |= ETH_FLAG_NTUPLE;
281*4882a593Smuzhiyun if (dev->features & NETIF_F_RXHASH)
282*4882a593Smuzhiyun flags |= ETH_FLAG_RXHASH;
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun return flags;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
__ethtool_set_flags(struct net_device * dev,u32 data)287*4882a593Smuzhiyun static int __ethtool_set_flags(struct net_device *dev, u32 data)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun netdev_features_t features = 0, changed;
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun if (data & ~ETH_ALL_FLAGS)
292*4882a593Smuzhiyun return -EINVAL;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun if (data & ETH_FLAG_LRO)
295*4882a593Smuzhiyun features |= NETIF_F_LRO;
296*4882a593Smuzhiyun if (data & ETH_FLAG_RXVLAN)
297*4882a593Smuzhiyun features |= NETIF_F_HW_VLAN_CTAG_RX;
298*4882a593Smuzhiyun if (data & ETH_FLAG_TXVLAN)
299*4882a593Smuzhiyun features |= NETIF_F_HW_VLAN_CTAG_TX;
300*4882a593Smuzhiyun if (data & ETH_FLAG_NTUPLE)
301*4882a593Smuzhiyun features |= NETIF_F_NTUPLE;
302*4882a593Smuzhiyun if (data & ETH_FLAG_RXHASH)
303*4882a593Smuzhiyun features |= NETIF_F_RXHASH;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun /* allow changing only bits set in hw_features */
306*4882a593Smuzhiyun changed = (features ^ dev->features) & ETH_ALL_FEATURES;
307*4882a593Smuzhiyun if (changed & ~dev->hw_features)
308*4882a593Smuzhiyun return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun dev->wanted_features =
311*4882a593Smuzhiyun (dev->wanted_features & ~changed) | (features & changed);
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun __netdev_update_features(dev);
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun return 0;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun /* Given two link masks, AND them together and save the result in dst. */
ethtool_intersect_link_masks(struct ethtool_link_ksettings * dst,struct ethtool_link_ksettings * src)319*4882a593Smuzhiyun void ethtool_intersect_link_masks(struct ethtool_link_ksettings *dst,
320*4882a593Smuzhiyun struct ethtool_link_ksettings *src)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun unsigned int size = BITS_TO_LONGS(__ETHTOOL_LINK_MODE_MASK_NBITS);
323*4882a593Smuzhiyun unsigned int idx = 0;
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun for (; idx < size; idx++) {
326*4882a593Smuzhiyun dst->link_modes.supported[idx] &=
327*4882a593Smuzhiyun src->link_modes.supported[idx];
328*4882a593Smuzhiyun dst->link_modes.advertising[idx] &=
329*4882a593Smuzhiyun src->link_modes.advertising[idx];
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun EXPORT_SYMBOL(ethtool_intersect_link_masks);
333*4882a593Smuzhiyun
ethtool_convert_legacy_u32_to_link_mode(unsigned long * dst,u32 legacy_u32)334*4882a593Smuzhiyun void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst,
335*4882a593Smuzhiyun u32 legacy_u32)
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS);
338*4882a593Smuzhiyun dst[0] = legacy_u32;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun EXPORT_SYMBOL(ethtool_convert_legacy_u32_to_link_mode);
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun /* return false if src had higher bits set. lower bits always updated. */
ethtool_convert_link_mode_to_legacy_u32(u32 * legacy_u32,const unsigned long * src)343*4882a593Smuzhiyun bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
344*4882a593Smuzhiyun const unsigned long *src)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun bool retval = true;
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun /* TODO: following test will soon always be true */
349*4882a593Smuzhiyun if (__ETHTOOL_LINK_MODE_MASK_NBITS > 32) {
350*4882a593Smuzhiyun __ETHTOOL_DECLARE_LINK_MODE_MASK(ext);
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun bitmap_zero(ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
353*4882a593Smuzhiyun bitmap_fill(ext, 32);
354*4882a593Smuzhiyun bitmap_complement(ext, ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
355*4882a593Smuzhiyun if (bitmap_intersects(ext, src,
356*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS)) {
357*4882a593Smuzhiyun /* src mask goes beyond bit 31 */
358*4882a593Smuzhiyun retval = false;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun *legacy_u32 = src[0];
362*4882a593Smuzhiyun return retval;
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun EXPORT_SYMBOL(ethtool_convert_link_mode_to_legacy_u32);
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun /* return false if ksettings link modes had higher bits
367*4882a593Smuzhiyun * set. legacy_settings always updated (best effort)
368*4882a593Smuzhiyun */
369*4882a593Smuzhiyun static bool
convert_link_ksettings_to_legacy_settings(struct ethtool_cmd * legacy_settings,const struct ethtool_link_ksettings * link_ksettings)370*4882a593Smuzhiyun convert_link_ksettings_to_legacy_settings(
371*4882a593Smuzhiyun struct ethtool_cmd *legacy_settings,
372*4882a593Smuzhiyun const struct ethtool_link_ksettings *link_ksettings)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun bool retval = true;
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun memset(legacy_settings, 0, sizeof(*legacy_settings));
377*4882a593Smuzhiyun /* this also clears the deprecated fields in legacy structure:
378*4882a593Smuzhiyun * __u8 transceiver;
379*4882a593Smuzhiyun * __u32 maxtxpkt;
380*4882a593Smuzhiyun * __u32 maxrxpkt;
381*4882a593Smuzhiyun */
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun retval &= ethtool_convert_link_mode_to_legacy_u32(
384*4882a593Smuzhiyun &legacy_settings->supported,
385*4882a593Smuzhiyun link_ksettings->link_modes.supported);
386*4882a593Smuzhiyun retval &= ethtool_convert_link_mode_to_legacy_u32(
387*4882a593Smuzhiyun &legacy_settings->advertising,
388*4882a593Smuzhiyun link_ksettings->link_modes.advertising);
389*4882a593Smuzhiyun retval &= ethtool_convert_link_mode_to_legacy_u32(
390*4882a593Smuzhiyun &legacy_settings->lp_advertising,
391*4882a593Smuzhiyun link_ksettings->link_modes.lp_advertising);
392*4882a593Smuzhiyun ethtool_cmd_speed_set(legacy_settings, link_ksettings->base.speed);
393*4882a593Smuzhiyun legacy_settings->duplex
394*4882a593Smuzhiyun = link_ksettings->base.duplex;
395*4882a593Smuzhiyun legacy_settings->port
396*4882a593Smuzhiyun = link_ksettings->base.port;
397*4882a593Smuzhiyun legacy_settings->phy_address
398*4882a593Smuzhiyun = link_ksettings->base.phy_address;
399*4882a593Smuzhiyun legacy_settings->autoneg
400*4882a593Smuzhiyun = link_ksettings->base.autoneg;
401*4882a593Smuzhiyun legacy_settings->mdio_support
402*4882a593Smuzhiyun = link_ksettings->base.mdio_support;
403*4882a593Smuzhiyun legacy_settings->eth_tp_mdix
404*4882a593Smuzhiyun = link_ksettings->base.eth_tp_mdix;
405*4882a593Smuzhiyun legacy_settings->eth_tp_mdix_ctrl
406*4882a593Smuzhiyun = link_ksettings->base.eth_tp_mdix_ctrl;
407*4882a593Smuzhiyun legacy_settings->transceiver
408*4882a593Smuzhiyun = link_ksettings->base.transceiver;
409*4882a593Smuzhiyun return retval;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun /* number of 32-bit words to store the user's link mode bitmaps */
413*4882a593Smuzhiyun #define __ETHTOOL_LINK_MODE_MASK_NU32 \
414*4882a593Smuzhiyun DIV_ROUND_UP(__ETHTOOL_LINK_MODE_MASK_NBITS, 32)
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun /* layout of the struct passed from/to userland */
417*4882a593Smuzhiyun struct ethtool_link_usettings {
418*4882a593Smuzhiyun struct ethtool_link_settings base;
419*4882a593Smuzhiyun struct {
420*4882a593Smuzhiyun __u32 supported[__ETHTOOL_LINK_MODE_MASK_NU32];
421*4882a593Smuzhiyun __u32 advertising[__ETHTOOL_LINK_MODE_MASK_NU32];
422*4882a593Smuzhiyun __u32 lp_advertising[__ETHTOOL_LINK_MODE_MASK_NU32];
423*4882a593Smuzhiyun } link_modes;
424*4882a593Smuzhiyun };
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun /* Internal kernel helper to query a device ethtool_link_settings. */
__ethtool_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * link_ksettings)427*4882a593Smuzhiyun int __ethtool_get_link_ksettings(struct net_device *dev,
428*4882a593Smuzhiyun struct ethtool_link_ksettings *link_ksettings)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun ASSERT_RTNL();
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun if (!dev->ethtool_ops->get_link_ksettings)
433*4882a593Smuzhiyun return -EOPNOTSUPP;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun memset(link_ksettings, 0, sizeof(*link_ksettings));
436*4882a593Smuzhiyun return dev->ethtool_ops->get_link_ksettings(dev, link_ksettings);
437*4882a593Smuzhiyun }
438*4882a593Smuzhiyun EXPORT_SYMBOL(__ethtool_get_link_ksettings);
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun /* convert ethtool_link_usettings in user space to a kernel internal
441*4882a593Smuzhiyun * ethtool_link_ksettings. return 0 on success, errno on error.
442*4882a593Smuzhiyun */
load_link_ksettings_from_user(struct ethtool_link_ksettings * to,const void __user * from)443*4882a593Smuzhiyun static int load_link_ksettings_from_user(struct ethtool_link_ksettings *to,
444*4882a593Smuzhiyun const void __user *from)
445*4882a593Smuzhiyun {
446*4882a593Smuzhiyun struct ethtool_link_usettings link_usettings;
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun if (copy_from_user(&link_usettings, from, sizeof(link_usettings)))
449*4882a593Smuzhiyun return -EFAULT;
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun memcpy(&to->base, &link_usettings.base, sizeof(to->base));
452*4882a593Smuzhiyun bitmap_from_arr32(to->link_modes.supported,
453*4882a593Smuzhiyun link_usettings.link_modes.supported,
454*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS);
455*4882a593Smuzhiyun bitmap_from_arr32(to->link_modes.advertising,
456*4882a593Smuzhiyun link_usettings.link_modes.advertising,
457*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS);
458*4882a593Smuzhiyun bitmap_from_arr32(to->link_modes.lp_advertising,
459*4882a593Smuzhiyun link_usettings.link_modes.lp_advertising,
460*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS);
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun return 0;
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun /* Check if the user is trying to change anything besides speed/duplex */
ethtool_virtdev_validate_cmd(const struct ethtool_link_ksettings * cmd)466*4882a593Smuzhiyun bool ethtool_virtdev_validate_cmd(const struct ethtool_link_ksettings *cmd)
467*4882a593Smuzhiyun {
468*4882a593Smuzhiyun struct ethtool_link_settings base2 = {};
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun base2.speed = cmd->base.speed;
471*4882a593Smuzhiyun base2.port = PORT_OTHER;
472*4882a593Smuzhiyun base2.duplex = cmd->base.duplex;
473*4882a593Smuzhiyun base2.cmd = cmd->base.cmd;
474*4882a593Smuzhiyun base2.link_mode_masks_nwords = cmd->base.link_mode_masks_nwords;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun return !memcmp(&base2, &cmd->base, sizeof(base2)) &&
477*4882a593Smuzhiyun bitmap_empty(cmd->link_modes.supported,
478*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS) &&
479*4882a593Smuzhiyun bitmap_empty(cmd->link_modes.lp_advertising,
480*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS);
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun /* convert a kernel internal ethtool_link_ksettings to
484*4882a593Smuzhiyun * ethtool_link_usettings in user space. return 0 on success, errno on
485*4882a593Smuzhiyun * error.
486*4882a593Smuzhiyun */
487*4882a593Smuzhiyun static int
store_link_ksettings_for_user(void __user * to,const struct ethtool_link_ksettings * from)488*4882a593Smuzhiyun store_link_ksettings_for_user(void __user *to,
489*4882a593Smuzhiyun const struct ethtool_link_ksettings *from)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun struct ethtool_link_usettings link_usettings;
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun memcpy(&link_usettings, from, sizeof(link_usettings));
494*4882a593Smuzhiyun bitmap_to_arr32(link_usettings.link_modes.supported,
495*4882a593Smuzhiyun from->link_modes.supported,
496*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS);
497*4882a593Smuzhiyun bitmap_to_arr32(link_usettings.link_modes.advertising,
498*4882a593Smuzhiyun from->link_modes.advertising,
499*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS);
500*4882a593Smuzhiyun bitmap_to_arr32(link_usettings.link_modes.lp_advertising,
501*4882a593Smuzhiyun from->link_modes.lp_advertising,
502*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NBITS);
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun if (copy_to_user(to, &link_usettings, sizeof(link_usettings)))
505*4882a593Smuzhiyun return -EFAULT;
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun return 0;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun
510*4882a593Smuzhiyun /* Query device for its ethtool_link_settings. */
ethtool_get_link_ksettings(struct net_device * dev,void __user * useraddr)511*4882a593Smuzhiyun static int ethtool_get_link_ksettings(struct net_device *dev,
512*4882a593Smuzhiyun void __user *useraddr)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun int err = 0;
515*4882a593Smuzhiyun struct ethtool_link_ksettings link_ksettings;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun ASSERT_RTNL();
518*4882a593Smuzhiyun if (!dev->ethtool_ops->get_link_ksettings)
519*4882a593Smuzhiyun return -EOPNOTSUPP;
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun /* handle bitmap nbits handshake */
522*4882a593Smuzhiyun if (copy_from_user(&link_ksettings.base, useraddr,
523*4882a593Smuzhiyun sizeof(link_ksettings.base)))
524*4882a593Smuzhiyun return -EFAULT;
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun if (__ETHTOOL_LINK_MODE_MASK_NU32
527*4882a593Smuzhiyun != link_ksettings.base.link_mode_masks_nwords) {
528*4882a593Smuzhiyun /* wrong link mode nbits requested */
529*4882a593Smuzhiyun memset(&link_ksettings, 0, sizeof(link_ksettings));
530*4882a593Smuzhiyun link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
531*4882a593Smuzhiyun /* send back number of words required as negative val */
532*4882a593Smuzhiyun compiletime_assert(__ETHTOOL_LINK_MODE_MASK_NU32 <= S8_MAX,
533*4882a593Smuzhiyun "need too many bits for link modes!");
534*4882a593Smuzhiyun link_ksettings.base.link_mode_masks_nwords
535*4882a593Smuzhiyun = -((s8)__ETHTOOL_LINK_MODE_MASK_NU32);
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun /* copy the base fields back to user, not the link
538*4882a593Smuzhiyun * mode bitmaps
539*4882a593Smuzhiyun */
540*4882a593Smuzhiyun if (copy_to_user(useraddr, &link_ksettings.base,
541*4882a593Smuzhiyun sizeof(link_ksettings.base)))
542*4882a593Smuzhiyun return -EFAULT;
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun return 0;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun /* handshake successful: user/kernel agree on
548*4882a593Smuzhiyun * link_mode_masks_nwords
549*4882a593Smuzhiyun */
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun memset(&link_ksettings, 0, sizeof(link_ksettings));
552*4882a593Smuzhiyun err = dev->ethtool_ops->get_link_ksettings(dev, &link_ksettings);
553*4882a593Smuzhiyun if (err < 0)
554*4882a593Smuzhiyun return err;
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun /* make sure we tell the right values to user */
557*4882a593Smuzhiyun link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
558*4882a593Smuzhiyun link_ksettings.base.link_mode_masks_nwords
559*4882a593Smuzhiyun = __ETHTOOL_LINK_MODE_MASK_NU32;
560*4882a593Smuzhiyun link_ksettings.base.master_slave_cfg = MASTER_SLAVE_CFG_UNSUPPORTED;
561*4882a593Smuzhiyun link_ksettings.base.master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun return store_link_ksettings_for_user(useraddr, &link_ksettings);
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun /* Update device ethtool_link_settings. */
ethtool_set_link_ksettings(struct net_device * dev,void __user * useraddr)567*4882a593Smuzhiyun static int ethtool_set_link_ksettings(struct net_device *dev,
568*4882a593Smuzhiyun void __user *useraddr)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun int err;
571*4882a593Smuzhiyun struct ethtool_link_ksettings link_ksettings;
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun ASSERT_RTNL();
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun if (!dev->ethtool_ops->set_link_ksettings)
576*4882a593Smuzhiyun return -EOPNOTSUPP;
577*4882a593Smuzhiyun
578*4882a593Smuzhiyun /* make sure nbits field has expected value */
579*4882a593Smuzhiyun if (copy_from_user(&link_ksettings.base, useraddr,
580*4882a593Smuzhiyun sizeof(link_ksettings.base)))
581*4882a593Smuzhiyun return -EFAULT;
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun if (__ETHTOOL_LINK_MODE_MASK_NU32
584*4882a593Smuzhiyun != link_ksettings.base.link_mode_masks_nwords)
585*4882a593Smuzhiyun return -EINVAL;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun /* copy the whole structure, now that we know it has expected
588*4882a593Smuzhiyun * format
589*4882a593Smuzhiyun */
590*4882a593Smuzhiyun err = load_link_ksettings_from_user(&link_ksettings, useraddr);
591*4882a593Smuzhiyun if (err)
592*4882a593Smuzhiyun return err;
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun /* re-check nwords field, just in case */
595*4882a593Smuzhiyun if (__ETHTOOL_LINK_MODE_MASK_NU32
596*4882a593Smuzhiyun != link_ksettings.base.link_mode_masks_nwords)
597*4882a593Smuzhiyun return -EINVAL;
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun if (link_ksettings.base.master_slave_cfg ||
600*4882a593Smuzhiyun link_ksettings.base.master_slave_state)
601*4882a593Smuzhiyun return -EINVAL;
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
604*4882a593Smuzhiyun if (err >= 0) {
605*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
606*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL);
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun return err;
609*4882a593Smuzhiyun }
610*4882a593Smuzhiyun
ethtool_virtdev_set_link_ksettings(struct net_device * dev,const struct ethtool_link_ksettings * cmd,u32 * dev_speed,u8 * dev_duplex)611*4882a593Smuzhiyun int ethtool_virtdev_set_link_ksettings(struct net_device *dev,
612*4882a593Smuzhiyun const struct ethtool_link_ksettings *cmd,
613*4882a593Smuzhiyun u32 *dev_speed, u8 *dev_duplex)
614*4882a593Smuzhiyun {
615*4882a593Smuzhiyun u32 speed;
616*4882a593Smuzhiyun u8 duplex;
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun speed = cmd->base.speed;
619*4882a593Smuzhiyun duplex = cmd->base.duplex;
620*4882a593Smuzhiyun /* don't allow custom speed and duplex */
621*4882a593Smuzhiyun if (!ethtool_validate_speed(speed) ||
622*4882a593Smuzhiyun !ethtool_validate_duplex(duplex) ||
623*4882a593Smuzhiyun !ethtool_virtdev_validate_cmd(cmd))
624*4882a593Smuzhiyun return -EINVAL;
625*4882a593Smuzhiyun *dev_speed = speed;
626*4882a593Smuzhiyun *dev_duplex = duplex;
627*4882a593Smuzhiyun
628*4882a593Smuzhiyun return 0;
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun EXPORT_SYMBOL(ethtool_virtdev_set_link_ksettings);
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun /* Query device for its ethtool_cmd settings.
633*4882a593Smuzhiyun *
634*4882a593Smuzhiyun * Backward compatibility note: for compatibility with legacy ethtool, this is
635*4882a593Smuzhiyun * now implemented via get_link_ksettings. When driver reports higher link mode
636*4882a593Smuzhiyun * bits, a kernel warning is logged once (with name of 1st driver/device) to
637*4882a593Smuzhiyun * recommend user to upgrade ethtool, but the command is successful (only the
638*4882a593Smuzhiyun * lower link mode bits reported back to user). Deprecated fields from
639*4882a593Smuzhiyun * ethtool_cmd (transceiver/maxrxpkt/maxtxpkt) are always set to zero.
640*4882a593Smuzhiyun */
ethtool_get_settings(struct net_device * dev,void __user * useraddr)641*4882a593Smuzhiyun static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun struct ethtool_link_ksettings link_ksettings;
644*4882a593Smuzhiyun struct ethtool_cmd cmd;
645*4882a593Smuzhiyun int err;
646*4882a593Smuzhiyun
647*4882a593Smuzhiyun ASSERT_RTNL();
648*4882a593Smuzhiyun if (!dev->ethtool_ops->get_link_ksettings)
649*4882a593Smuzhiyun return -EOPNOTSUPP;
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun memset(&link_ksettings, 0, sizeof(link_ksettings));
652*4882a593Smuzhiyun err = dev->ethtool_ops->get_link_ksettings(dev, &link_ksettings);
653*4882a593Smuzhiyun if (err < 0)
654*4882a593Smuzhiyun return err;
655*4882a593Smuzhiyun convert_link_ksettings_to_legacy_settings(&cmd, &link_ksettings);
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun /* send a sensible cmd tag back to user */
658*4882a593Smuzhiyun cmd.cmd = ETHTOOL_GSET;
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
661*4882a593Smuzhiyun return -EFAULT;
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun return 0;
664*4882a593Smuzhiyun }
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun /* Update device link settings with given ethtool_cmd.
667*4882a593Smuzhiyun *
668*4882a593Smuzhiyun * Backward compatibility note: for compatibility with legacy ethtool, this is
669*4882a593Smuzhiyun * now always implemented via set_link_settings. When user's request updates
670*4882a593Smuzhiyun * deprecated ethtool_cmd fields (transceiver/maxrxpkt/maxtxpkt), a kernel
671*4882a593Smuzhiyun * warning is logged once (with name of 1st driver/device) to recommend user to
672*4882a593Smuzhiyun * upgrade ethtool, and the request is rejected.
673*4882a593Smuzhiyun */
ethtool_set_settings(struct net_device * dev,void __user * useraddr)674*4882a593Smuzhiyun static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun struct ethtool_link_ksettings link_ksettings;
677*4882a593Smuzhiyun struct ethtool_cmd cmd;
678*4882a593Smuzhiyun int ret;
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun ASSERT_RTNL();
681*4882a593Smuzhiyun
682*4882a593Smuzhiyun if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
683*4882a593Smuzhiyun return -EFAULT;
684*4882a593Smuzhiyun if (!dev->ethtool_ops->set_link_ksettings)
685*4882a593Smuzhiyun return -EOPNOTSUPP;
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun if (!convert_legacy_settings_to_link_ksettings(&link_ksettings, &cmd))
688*4882a593Smuzhiyun return -EINVAL;
689*4882a593Smuzhiyun link_ksettings.base.link_mode_masks_nwords =
690*4882a593Smuzhiyun __ETHTOOL_LINK_MODE_MASK_NU32;
691*4882a593Smuzhiyun ret = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
692*4882a593Smuzhiyun if (ret >= 0) {
693*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
694*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL);
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun return ret;
697*4882a593Smuzhiyun }
698*4882a593Smuzhiyun
ethtool_get_drvinfo(struct net_device * dev,void __user * useraddr)699*4882a593Smuzhiyun static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
700*4882a593Smuzhiyun void __user *useraddr)
701*4882a593Smuzhiyun {
702*4882a593Smuzhiyun struct ethtool_drvinfo info;
703*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun memset(&info, 0, sizeof(info));
706*4882a593Smuzhiyun info.cmd = ETHTOOL_GDRVINFO;
707*4882a593Smuzhiyun strlcpy(info.version, UTS_RELEASE, sizeof(info.version));
708*4882a593Smuzhiyun if (ops->get_drvinfo) {
709*4882a593Smuzhiyun ops->get_drvinfo(dev, &info);
710*4882a593Smuzhiyun } else if (dev->dev.parent && dev->dev.parent->driver) {
711*4882a593Smuzhiyun strlcpy(info.bus_info, dev_name(dev->dev.parent),
712*4882a593Smuzhiyun sizeof(info.bus_info));
713*4882a593Smuzhiyun strlcpy(info.driver, dev->dev.parent->driver->name,
714*4882a593Smuzhiyun sizeof(info.driver));
715*4882a593Smuzhiyun } else {
716*4882a593Smuzhiyun return -EOPNOTSUPP;
717*4882a593Smuzhiyun }
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun /*
720*4882a593Smuzhiyun * this method of obtaining string set info is deprecated;
721*4882a593Smuzhiyun * Use ETHTOOL_GSSET_INFO instead.
722*4882a593Smuzhiyun */
723*4882a593Smuzhiyun if (ops->get_sset_count) {
724*4882a593Smuzhiyun int rc;
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun rc = ops->get_sset_count(dev, ETH_SS_TEST);
727*4882a593Smuzhiyun if (rc >= 0)
728*4882a593Smuzhiyun info.testinfo_len = rc;
729*4882a593Smuzhiyun rc = ops->get_sset_count(dev, ETH_SS_STATS);
730*4882a593Smuzhiyun if (rc >= 0)
731*4882a593Smuzhiyun info.n_stats = rc;
732*4882a593Smuzhiyun rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
733*4882a593Smuzhiyun if (rc >= 0)
734*4882a593Smuzhiyun info.n_priv_flags = rc;
735*4882a593Smuzhiyun }
736*4882a593Smuzhiyun if (ops->get_regs_len) {
737*4882a593Smuzhiyun int ret = ops->get_regs_len(dev);
738*4882a593Smuzhiyun
739*4882a593Smuzhiyun if (ret > 0)
740*4882a593Smuzhiyun info.regdump_len = ret;
741*4882a593Smuzhiyun }
742*4882a593Smuzhiyun
743*4882a593Smuzhiyun if (ops->get_eeprom_len)
744*4882a593Smuzhiyun info.eedump_len = ops->get_eeprom_len(dev);
745*4882a593Smuzhiyun
746*4882a593Smuzhiyun if (!info.fw_version[0])
747*4882a593Smuzhiyun devlink_compat_running_version(dev, info.fw_version,
748*4882a593Smuzhiyun sizeof(info.fw_version));
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun if (copy_to_user(useraddr, &info, sizeof(info)))
751*4882a593Smuzhiyun return -EFAULT;
752*4882a593Smuzhiyun return 0;
753*4882a593Smuzhiyun }
754*4882a593Smuzhiyun
ethtool_get_sset_info(struct net_device * dev,void __user * useraddr)755*4882a593Smuzhiyun static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
756*4882a593Smuzhiyun void __user *useraddr)
757*4882a593Smuzhiyun {
758*4882a593Smuzhiyun struct ethtool_sset_info info;
759*4882a593Smuzhiyun u64 sset_mask;
760*4882a593Smuzhiyun int i, idx = 0, n_bits = 0, ret, rc;
761*4882a593Smuzhiyun u32 *info_buf = NULL;
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun if (copy_from_user(&info, useraddr, sizeof(info)))
764*4882a593Smuzhiyun return -EFAULT;
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun /* store copy of mask, because we zero struct later on */
767*4882a593Smuzhiyun sset_mask = info.sset_mask;
768*4882a593Smuzhiyun if (!sset_mask)
769*4882a593Smuzhiyun return 0;
770*4882a593Smuzhiyun
771*4882a593Smuzhiyun /* calculate size of return buffer */
772*4882a593Smuzhiyun n_bits = hweight64(sset_mask);
773*4882a593Smuzhiyun
774*4882a593Smuzhiyun memset(&info, 0, sizeof(info));
775*4882a593Smuzhiyun info.cmd = ETHTOOL_GSSET_INFO;
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun info_buf = kcalloc(n_bits, sizeof(u32), GFP_USER);
778*4882a593Smuzhiyun if (!info_buf)
779*4882a593Smuzhiyun return -ENOMEM;
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun /*
782*4882a593Smuzhiyun * fill return buffer based on input bitmask and successful
783*4882a593Smuzhiyun * get_sset_count return
784*4882a593Smuzhiyun */
785*4882a593Smuzhiyun for (i = 0; i < 64; i++) {
786*4882a593Smuzhiyun if (!(sset_mask & (1ULL << i)))
787*4882a593Smuzhiyun continue;
788*4882a593Smuzhiyun
789*4882a593Smuzhiyun rc = __ethtool_get_sset_count(dev, i);
790*4882a593Smuzhiyun if (rc >= 0) {
791*4882a593Smuzhiyun info.sset_mask |= (1ULL << i);
792*4882a593Smuzhiyun info_buf[idx++] = rc;
793*4882a593Smuzhiyun }
794*4882a593Smuzhiyun }
795*4882a593Smuzhiyun
796*4882a593Smuzhiyun ret = -EFAULT;
797*4882a593Smuzhiyun if (copy_to_user(useraddr, &info, sizeof(info)))
798*4882a593Smuzhiyun goto out;
799*4882a593Smuzhiyun
800*4882a593Smuzhiyun useraddr += offsetof(struct ethtool_sset_info, data);
801*4882a593Smuzhiyun if (copy_to_user(useraddr, info_buf, idx * sizeof(u32)))
802*4882a593Smuzhiyun goto out;
803*4882a593Smuzhiyun
804*4882a593Smuzhiyun ret = 0;
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun out:
807*4882a593Smuzhiyun kfree(info_buf);
808*4882a593Smuzhiyun return ret;
809*4882a593Smuzhiyun }
810*4882a593Smuzhiyun
811*4882a593Smuzhiyun static noinline_for_stack int
ethtool_rxnfc_copy_from_compat(struct ethtool_rxnfc * rxnfc,const struct compat_ethtool_rxnfc __user * useraddr,size_t size)812*4882a593Smuzhiyun ethtool_rxnfc_copy_from_compat(struct ethtool_rxnfc *rxnfc,
813*4882a593Smuzhiyun const struct compat_ethtool_rxnfc __user *useraddr,
814*4882a593Smuzhiyun size_t size)
815*4882a593Smuzhiyun {
816*4882a593Smuzhiyun struct compat_ethtool_rxnfc crxnfc = {};
817*4882a593Smuzhiyun
818*4882a593Smuzhiyun /* We expect there to be holes between fs.m_ext and
819*4882a593Smuzhiyun * fs.ring_cookie and at the end of fs, but nowhere else.
820*4882a593Smuzhiyun * On non-x86, no conversion should be needed.
821*4882a593Smuzhiyun */
822*4882a593Smuzhiyun BUILD_BUG_ON(!IS_ENABLED(CONFIG_X86_64) &&
823*4882a593Smuzhiyun sizeof(struct compat_ethtool_rxnfc) !=
824*4882a593Smuzhiyun sizeof(struct ethtool_rxnfc));
825*4882a593Smuzhiyun BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_ext) +
826*4882a593Smuzhiyun sizeof(useraddr->fs.m_ext) !=
827*4882a593Smuzhiyun offsetof(struct ethtool_rxnfc, fs.m_ext) +
828*4882a593Smuzhiyun sizeof(rxnfc->fs.m_ext));
829*4882a593Smuzhiyun BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.location) -
830*4882a593Smuzhiyun offsetof(struct compat_ethtool_rxnfc, fs.ring_cookie) !=
831*4882a593Smuzhiyun offsetof(struct ethtool_rxnfc, fs.location) -
832*4882a593Smuzhiyun offsetof(struct ethtool_rxnfc, fs.ring_cookie));
833*4882a593Smuzhiyun
834*4882a593Smuzhiyun if (copy_from_user(&crxnfc, useraddr, min(size, sizeof(crxnfc))))
835*4882a593Smuzhiyun return -EFAULT;
836*4882a593Smuzhiyun
837*4882a593Smuzhiyun *rxnfc = (struct ethtool_rxnfc) {
838*4882a593Smuzhiyun .cmd = crxnfc.cmd,
839*4882a593Smuzhiyun .flow_type = crxnfc.flow_type,
840*4882a593Smuzhiyun .data = crxnfc.data,
841*4882a593Smuzhiyun .fs = {
842*4882a593Smuzhiyun .flow_type = crxnfc.fs.flow_type,
843*4882a593Smuzhiyun .h_u = crxnfc.fs.h_u,
844*4882a593Smuzhiyun .h_ext = crxnfc.fs.h_ext,
845*4882a593Smuzhiyun .m_u = crxnfc.fs.m_u,
846*4882a593Smuzhiyun .m_ext = crxnfc.fs.m_ext,
847*4882a593Smuzhiyun .ring_cookie = crxnfc.fs.ring_cookie,
848*4882a593Smuzhiyun .location = crxnfc.fs.location,
849*4882a593Smuzhiyun },
850*4882a593Smuzhiyun .rule_cnt = crxnfc.rule_cnt,
851*4882a593Smuzhiyun };
852*4882a593Smuzhiyun
853*4882a593Smuzhiyun return 0;
854*4882a593Smuzhiyun }
855*4882a593Smuzhiyun
ethtool_rxnfc_copy_from_user(struct ethtool_rxnfc * rxnfc,const void __user * useraddr,size_t size)856*4882a593Smuzhiyun static int ethtool_rxnfc_copy_from_user(struct ethtool_rxnfc *rxnfc,
857*4882a593Smuzhiyun const void __user *useraddr,
858*4882a593Smuzhiyun size_t size)
859*4882a593Smuzhiyun {
860*4882a593Smuzhiyun if (compat_need_64bit_alignment_fixup())
861*4882a593Smuzhiyun return ethtool_rxnfc_copy_from_compat(rxnfc, useraddr, size);
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun if (copy_from_user(rxnfc, useraddr, size))
864*4882a593Smuzhiyun return -EFAULT;
865*4882a593Smuzhiyun
866*4882a593Smuzhiyun return 0;
867*4882a593Smuzhiyun }
868*4882a593Smuzhiyun
ethtool_rxnfc_copy_to_compat(void __user * useraddr,const struct ethtool_rxnfc * rxnfc,size_t size,const u32 * rule_buf)869*4882a593Smuzhiyun static int ethtool_rxnfc_copy_to_compat(void __user *useraddr,
870*4882a593Smuzhiyun const struct ethtool_rxnfc *rxnfc,
871*4882a593Smuzhiyun size_t size, const u32 *rule_buf)
872*4882a593Smuzhiyun {
873*4882a593Smuzhiyun struct compat_ethtool_rxnfc crxnfc;
874*4882a593Smuzhiyun
875*4882a593Smuzhiyun memset(&crxnfc, 0, sizeof(crxnfc));
876*4882a593Smuzhiyun crxnfc = (struct compat_ethtool_rxnfc) {
877*4882a593Smuzhiyun .cmd = rxnfc->cmd,
878*4882a593Smuzhiyun .flow_type = rxnfc->flow_type,
879*4882a593Smuzhiyun .data = rxnfc->data,
880*4882a593Smuzhiyun .fs = {
881*4882a593Smuzhiyun .flow_type = rxnfc->fs.flow_type,
882*4882a593Smuzhiyun .h_u = rxnfc->fs.h_u,
883*4882a593Smuzhiyun .h_ext = rxnfc->fs.h_ext,
884*4882a593Smuzhiyun .m_u = rxnfc->fs.m_u,
885*4882a593Smuzhiyun .m_ext = rxnfc->fs.m_ext,
886*4882a593Smuzhiyun .ring_cookie = rxnfc->fs.ring_cookie,
887*4882a593Smuzhiyun .location = rxnfc->fs.location,
888*4882a593Smuzhiyun },
889*4882a593Smuzhiyun .rule_cnt = rxnfc->rule_cnt,
890*4882a593Smuzhiyun };
891*4882a593Smuzhiyun
892*4882a593Smuzhiyun if (copy_to_user(useraddr, &crxnfc, min(size, sizeof(crxnfc))))
893*4882a593Smuzhiyun return -EFAULT;
894*4882a593Smuzhiyun
895*4882a593Smuzhiyun return 0;
896*4882a593Smuzhiyun }
897*4882a593Smuzhiyun
ethtool_rxnfc_copy_to_user(void __user * useraddr,const struct ethtool_rxnfc * rxnfc,size_t size,const u32 * rule_buf)898*4882a593Smuzhiyun static int ethtool_rxnfc_copy_to_user(void __user *useraddr,
899*4882a593Smuzhiyun const struct ethtool_rxnfc *rxnfc,
900*4882a593Smuzhiyun size_t size, const u32 *rule_buf)
901*4882a593Smuzhiyun {
902*4882a593Smuzhiyun int ret;
903*4882a593Smuzhiyun
904*4882a593Smuzhiyun if (compat_need_64bit_alignment_fixup()) {
905*4882a593Smuzhiyun ret = ethtool_rxnfc_copy_to_compat(useraddr, rxnfc, size,
906*4882a593Smuzhiyun rule_buf);
907*4882a593Smuzhiyun useraddr += offsetof(struct compat_ethtool_rxnfc, rule_locs);
908*4882a593Smuzhiyun } else {
909*4882a593Smuzhiyun ret = copy_to_user(useraddr, rxnfc, size);
910*4882a593Smuzhiyun useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
911*4882a593Smuzhiyun }
912*4882a593Smuzhiyun
913*4882a593Smuzhiyun if (ret)
914*4882a593Smuzhiyun return -EFAULT;
915*4882a593Smuzhiyun
916*4882a593Smuzhiyun if (rule_buf) {
917*4882a593Smuzhiyun if (copy_to_user(useraddr, rule_buf,
918*4882a593Smuzhiyun rxnfc->rule_cnt * sizeof(u32)))
919*4882a593Smuzhiyun return -EFAULT;
920*4882a593Smuzhiyun }
921*4882a593Smuzhiyun
922*4882a593Smuzhiyun return 0;
923*4882a593Smuzhiyun }
924*4882a593Smuzhiyun
ethtool_set_rxnfc(struct net_device * dev,u32 cmd,void __user * useraddr)925*4882a593Smuzhiyun static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
926*4882a593Smuzhiyun u32 cmd, void __user *useraddr)
927*4882a593Smuzhiyun {
928*4882a593Smuzhiyun struct ethtool_rxnfc info;
929*4882a593Smuzhiyun size_t info_size = sizeof(info);
930*4882a593Smuzhiyun int rc;
931*4882a593Smuzhiyun
932*4882a593Smuzhiyun if (!dev->ethtool_ops->set_rxnfc)
933*4882a593Smuzhiyun return -EOPNOTSUPP;
934*4882a593Smuzhiyun
935*4882a593Smuzhiyun /* struct ethtool_rxnfc was originally defined for
936*4882a593Smuzhiyun * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
937*4882a593Smuzhiyun * members. User-space might still be using that
938*4882a593Smuzhiyun * definition. */
939*4882a593Smuzhiyun if (cmd == ETHTOOL_SRXFH)
940*4882a593Smuzhiyun info_size = (offsetof(struct ethtool_rxnfc, data) +
941*4882a593Smuzhiyun sizeof(info.data));
942*4882a593Smuzhiyun
943*4882a593Smuzhiyun if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
944*4882a593Smuzhiyun return -EFAULT;
945*4882a593Smuzhiyun
946*4882a593Smuzhiyun rc = dev->ethtool_ops->set_rxnfc(dev, &info);
947*4882a593Smuzhiyun if (rc)
948*4882a593Smuzhiyun return rc;
949*4882a593Smuzhiyun
950*4882a593Smuzhiyun if (cmd == ETHTOOL_SRXCLSRLINS &&
951*4882a593Smuzhiyun ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL))
952*4882a593Smuzhiyun return -EFAULT;
953*4882a593Smuzhiyun
954*4882a593Smuzhiyun return 0;
955*4882a593Smuzhiyun }
956*4882a593Smuzhiyun
ethtool_get_rxnfc(struct net_device * dev,u32 cmd,void __user * useraddr)957*4882a593Smuzhiyun static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
958*4882a593Smuzhiyun u32 cmd, void __user *useraddr)
959*4882a593Smuzhiyun {
960*4882a593Smuzhiyun struct ethtool_rxnfc info;
961*4882a593Smuzhiyun size_t info_size = sizeof(info);
962*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
963*4882a593Smuzhiyun int ret;
964*4882a593Smuzhiyun void *rule_buf = NULL;
965*4882a593Smuzhiyun
966*4882a593Smuzhiyun if (!ops->get_rxnfc)
967*4882a593Smuzhiyun return -EOPNOTSUPP;
968*4882a593Smuzhiyun
969*4882a593Smuzhiyun /* struct ethtool_rxnfc was originally defined for
970*4882a593Smuzhiyun * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
971*4882a593Smuzhiyun * members. User-space might still be using that
972*4882a593Smuzhiyun * definition. */
973*4882a593Smuzhiyun if (cmd == ETHTOOL_GRXFH)
974*4882a593Smuzhiyun info_size = (offsetof(struct ethtool_rxnfc, data) +
975*4882a593Smuzhiyun sizeof(info.data));
976*4882a593Smuzhiyun
977*4882a593Smuzhiyun if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
978*4882a593Smuzhiyun return -EFAULT;
979*4882a593Smuzhiyun
980*4882a593Smuzhiyun /* If FLOW_RSS was requested then user-space must be using the
981*4882a593Smuzhiyun * new definition, as FLOW_RSS is newer.
982*4882a593Smuzhiyun */
983*4882a593Smuzhiyun if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) {
984*4882a593Smuzhiyun info_size = sizeof(info);
985*4882a593Smuzhiyun if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
986*4882a593Smuzhiyun return -EFAULT;
987*4882a593Smuzhiyun /* Since malicious users may modify the original data,
988*4882a593Smuzhiyun * we need to check whether FLOW_RSS is still requested.
989*4882a593Smuzhiyun */
990*4882a593Smuzhiyun if (!(info.flow_type & FLOW_RSS))
991*4882a593Smuzhiyun return -EINVAL;
992*4882a593Smuzhiyun }
993*4882a593Smuzhiyun
994*4882a593Smuzhiyun if (info.cmd != cmd)
995*4882a593Smuzhiyun return -EINVAL;
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun if (info.cmd == ETHTOOL_GRXCLSRLALL) {
998*4882a593Smuzhiyun if (info.rule_cnt > 0) {
999*4882a593Smuzhiyun if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
1000*4882a593Smuzhiyun rule_buf = kcalloc(info.rule_cnt, sizeof(u32),
1001*4882a593Smuzhiyun GFP_USER);
1002*4882a593Smuzhiyun if (!rule_buf)
1003*4882a593Smuzhiyun return -ENOMEM;
1004*4882a593Smuzhiyun }
1005*4882a593Smuzhiyun }
1006*4882a593Smuzhiyun
1007*4882a593Smuzhiyun ret = ops->get_rxnfc(dev, &info, rule_buf);
1008*4882a593Smuzhiyun if (ret < 0)
1009*4882a593Smuzhiyun goto err_out;
1010*4882a593Smuzhiyun
1011*4882a593Smuzhiyun ret = ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, rule_buf);
1012*4882a593Smuzhiyun err_out:
1013*4882a593Smuzhiyun kfree(rule_buf);
1014*4882a593Smuzhiyun
1015*4882a593Smuzhiyun return ret;
1016*4882a593Smuzhiyun }
1017*4882a593Smuzhiyun
ethtool_copy_validate_indir(u32 * indir,void __user * useraddr,struct ethtool_rxnfc * rx_rings,u32 size)1018*4882a593Smuzhiyun static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
1019*4882a593Smuzhiyun struct ethtool_rxnfc *rx_rings,
1020*4882a593Smuzhiyun u32 size)
1021*4882a593Smuzhiyun {
1022*4882a593Smuzhiyun int i;
1023*4882a593Smuzhiyun
1024*4882a593Smuzhiyun if (copy_from_user(indir, useraddr, size * sizeof(indir[0])))
1025*4882a593Smuzhiyun return -EFAULT;
1026*4882a593Smuzhiyun
1027*4882a593Smuzhiyun /* Validate ring indices */
1028*4882a593Smuzhiyun for (i = 0; i < size; i++)
1029*4882a593Smuzhiyun if (indir[i] >= rx_rings->data)
1030*4882a593Smuzhiyun return -EINVAL;
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun return 0;
1033*4882a593Smuzhiyun }
1034*4882a593Smuzhiyun
1035*4882a593Smuzhiyun u8 netdev_rss_key[NETDEV_RSS_KEY_LEN] __read_mostly;
1036*4882a593Smuzhiyun
netdev_rss_key_fill(void * buffer,size_t len)1037*4882a593Smuzhiyun void netdev_rss_key_fill(void *buffer, size_t len)
1038*4882a593Smuzhiyun {
1039*4882a593Smuzhiyun BUG_ON(len > sizeof(netdev_rss_key));
1040*4882a593Smuzhiyun net_get_random_once(netdev_rss_key, sizeof(netdev_rss_key));
1041*4882a593Smuzhiyun memcpy(buffer, netdev_rss_key, len);
1042*4882a593Smuzhiyun }
1043*4882a593Smuzhiyun EXPORT_SYMBOL(netdev_rss_key_fill);
1044*4882a593Smuzhiyun
ethtool_get_rxfh_indir(struct net_device * dev,void __user * useraddr)1045*4882a593Smuzhiyun static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
1046*4882a593Smuzhiyun void __user *useraddr)
1047*4882a593Smuzhiyun {
1048*4882a593Smuzhiyun u32 user_size, dev_size;
1049*4882a593Smuzhiyun u32 *indir;
1050*4882a593Smuzhiyun int ret;
1051*4882a593Smuzhiyun
1052*4882a593Smuzhiyun if (!dev->ethtool_ops->get_rxfh_indir_size ||
1053*4882a593Smuzhiyun !dev->ethtool_ops->get_rxfh)
1054*4882a593Smuzhiyun return -EOPNOTSUPP;
1055*4882a593Smuzhiyun dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
1056*4882a593Smuzhiyun if (dev_size == 0)
1057*4882a593Smuzhiyun return -EOPNOTSUPP;
1058*4882a593Smuzhiyun
1059*4882a593Smuzhiyun if (copy_from_user(&user_size,
1060*4882a593Smuzhiyun useraddr + offsetof(struct ethtool_rxfh_indir, size),
1061*4882a593Smuzhiyun sizeof(user_size)))
1062*4882a593Smuzhiyun return -EFAULT;
1063*4882a593Smuzhiyun
1064*4882a593Smuzhiyun if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size),
1065*4882a593Smuzhiyun &dev_size, sizeof(dev_size)))
1066*4882a593Smuzhiyun return -EFAULT;
1067*4882a593Smuzhiyun
1068*4882a593Smuzhiyun /* If the user buffer size is 0, this is just a query for the
1069*4882a593Smuzhiyun * device table size. Otherwise, if it's smaller than the
1070*4882a593Smuzhiyun * device table size it's an error.
1071*4882a593Smuzhiyun */
1072*4882a593Smuzhiyun if (user_size < dev_size)
1073*4882a593Smuzhiyun return user_size == 0 ? 0 : -EINVAL;
1074*4882a593Smuzhiyun
1075*4882a593Smuzhiyun indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
1076*4882a593Smuzhiyun if (!indir)
1077*4882a593Smuzhiyun return -ENOMEM;
1078*4882a593Smuzhiyun
1079*4882a593Smuzhiyun ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
1080*4882a593Smuzhiyun if (ret)
1081*4882a593Smuzhiyun goto out;
1082*4882a593Smuzhiyun
1083*4882a593Smuzhiyun if (copy_to_user(useraddr +
1084*4882a593Smuzhiyun offsetof(struct ethtool_rxfh_indir, ring_index[0]),
1085*4882a593Smuzhiyun indir, dev_size * sizeof(indir[0])))
1086*4882a593Smuzhiyun ret = -EFAULT;
1087*4882a593Smuzhiyun
1088*4882a593Smuzhiyun out:
1089*4882a593Smuzhiyun kfree(indir);
1090*4882a593Smuzhiyun return ret;
1091*4882a593Smuzhiyun }
1092*4882a593Smuzhiyun
ethtool_set_rxfh_indir(struct net_device * dev,void __user * useraddr)1093*4882a593Smuzhiyun static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
1094*4882a593Smuzhiyun void __user *useraddr)
1095*4882a593Smuzhiyun {
1096*4882a593Smuzhiyun struct ethtool_rxnfc rx_rings;
1097*4882a593Smuzhiyun u32 user_size, dev_size, i;
1098*4882a593Smuzhiyun u32 *indir;
1099*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1100*4882a593Smuzhiyun int ret;
1101*4882a593Smuzhiyun u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]);
1102*4882a593Smuzhiyun
1103*4882a593Smuzhiyun if (!ops->get_rxfh_indir_size || !ops->set_rxfh ||
1104*4882a593Smuzhiyun !ops->get_rxnfc)
1105*4882a593Smuzhiyun return -EOPNOTSUPP;
1106*4882a593Smuzhiyun
1107*4882a593Smuzhiyun dev_size = ops->get_rxfh_indir_size(dev);
1108*4882a593Smuzhiyun if (dev_size == 0)
1109*4882a593Smuzhiyun return -EOPNOTSUPP;
1110*4882a593Smuzhiyun
1111*4882a593Smuzhiyun if (copy_from_user(&user_size,
1112*4882a593Smuzhiyun useraddr + offsetof(struct ethtool_rxfh_indir, size),
1113*4882a593Smuzhiyun sizeof(user_size)))
1114*4882a593Smuzhiyun return -EFAULT;
1115*4882a593Smuzhiyun
1116*4882a593Smuzhiyun if (user_size != 0 && user_size != dev_size)
1117*4882a593Smuzhiyun return -EINVAL;
1118*4882a593Smuzhiyun
1119*4882a593Smuzhiyun indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
1120*4882a593Smuzhiyun if (!indir)
1121*4882a593Smuzhiyun return -ENOMEM;
1122*4882a593Smuzhiyun
1123*4882a593Smuzhiyun rx_rings.cmd = ETHTOOL_GRXRINGS;
1124*4882a593Smuzhiyun ret = ops->get_rxnfc(dev, &rx_rings, NULL);
1125*4882a593Smuzhiyun if (ret)
1126*4882a593Smuzhiyun goto out;
1127*4882a593Smuzhiyun
1128*4882a593Smuzhiyun if (user_size == 0) {
1129*4882a593Smuzhiyun for (i = 0; i < dev_size; i++)
1130*4882a593Smuzhiyun indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
1131*4882a593Smuzhiyun } else {
1132*4882a593Smuzhiyun ret = ethtool_copy_validate_indir(indir,
1133*4882a593Smuzhiyun useraddr + ringidx_offset,
1134*4882a593Smuzhiyun &rx_rings,
1135*4882a593Smuzhiyun dev_size);
1136*4882a593Smuzhiyun if (ret)
1137*4882a593Smuzhiyun goto out;
1138*4882a593Smuzhiyun }
1139*4882a593Smuzhiyun
1140*4882a593Smuzhiyun ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
1141*4882a593Smuzhiyun if (ret)
1142*4882a593Smuzhiyun goto out;
1143*4882a593Smuzhiyun
1144*4882a593Smuzhiyun /* indicate whether rxfh was set to default */
1145*4882a593Smuzhiyun if (user_size == 0)
1146*4882a593Smuzhiyun dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
1147*4882a593Smuzhiyun else
1148*4882a593Smuzhiyun dev->priv_flags |= IFF_RXFH_CONFIGURED;
1149*4882a593Smuzhiyun
1150*4882a593Smuzhiyun out:
1151*4882a593Smuzhiyun kfree(indir);
1152*4882a593Smuzhiyun return ret;
1153*4882a593Smuzhiyun }
1154*4882a593Smuzhiyun
ethtool_get_rxfh(struct net_device * dev,void __user * useraddr)1155*4882a593Smuzhiyun static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
1156*4882a593Smuzhiyun void __user *useraddr)
1157*4882a593Smuzhiyun {
1158*4882a593Smuzhiyun int ret;
1159*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1160*4882a593Smuzhiyun u32 user_indir_size, user_key_size;
1161*4882a593Smuzhiyun u32 dev_indir_size = 0, dev_key_size = 0;
1162*4882a593Smuzhiyun struct ethtool_rxfh rxfh;
1163*4882a593Smuzhiyun u32 total_size;
1164*4882a593Smuzhiyun u32 indir_bytes;
1165*4882a593Smuzhiyun u32 *indir = NULL;
1166*4882a593Smuzhiyun u8 dev_hfunc = 0;
1167*4882a593Smuzhiyun u8 *hkey = NULL;
1168*4882a593Smuzhiyun u8 *rss_config;
1169*4882a593Smuzhiyun
1170*4882a593Smuzhiyun if (!ops->get_rxfh)
1171*4882a593Smuzhiyun return -EOPNOTSUPP;
1172*4882a593Smuzhiyun
1173*4882a593Smuzhiyun if (ops->get_rxfh_indir_size)
1174*4882a593Smuzhiyun dev_indir_size = ops->get_rxfh_indir_size(dev);
1175*4882a593Smuzhiyun if (ops->get_rxfh_key_size)
1176*4882a593Smuzhiyun dev_key_size = ops->get_rxfh_key_size(dev);
1177*4882a593Smuzhiyun
1178*4882a593Smuzhiyun if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
1179*4882a593Smuzhiyun return -EFAULT;
1180*4882a593Smuzhiyun user_indir_size = rxfh.indir_size;
1181*4882a593Smuzhiyun user_key_size = rxfh.key_size;
1182*4882a593Smuzhiyun
1183*4882a593Smuzhiyun /* Check that reserved fields are 0 for now */
1184*4882a593Smuzhiyun if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32)
1185*4882a593Smuzhiyun return -EINVAL;
1186*4882a593Smuzhiyun /* Most drivers don't handle rss_context, check it's 0 as well */
1187*4882a593Smuzhiyun if (rxfh.rss_context && !ops->get_rxfh_context)
1188*4882a593Smuzhiyun return -EOPNOTSUPP;
1189*4882a593Smuzhiyun
1190*4882a593Smuzhiyun rxfh.indir_size = dev_indir_size;
1191*4882a593Smuzhiyun rxfh.key_size = dev_key_size;
1192*4882a593Smuzhiyun if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
1193*4882a593Smuzhiyun return -EFAULT;
1194*4882a593Smuzhiyun
1195*4882a593Smuzhiyun if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
1196*4882a593Smuzhiyun (user_key_size && (user_key_size != dev_key_size)))
1197*4882a593Smuzhiyun return -EINVAL;
1198*4882a593Smuzhiyun
1199*4882a593Smuzhiyun indir_bytes = user_indir_size * sizeof(indir[0]);
1200*4882a593Smuzhiyun total_size = indir_bytes + user_key_size;
1201*4882a593Smuzhiyun rss_config = kzalloc(total_size, GFP_USER);
1202*4882a593Smuzhiyun if (!rss_config)
1203*4882a593Smuzhiyun return -ENOMEM;
1204*4882a593Smuzhiyun
1205*4882a593Smuzhiyun if (user_indir_size)
1206*4882a593Smuzhiyun indir = (u32 *)rss_config;
1207*4882a593Smuzhiyun
1208*4882a593Smuzhiyun if (user_key_size)
1209*4882a593Smuzhiyun hkey = rss_config + indir_bytes;
1210*4882a593Smuzhiyun
1211*4882a593Smuzhiyun if (rxfh.rss_context)
1212*4882a593Smuzhiyun ret = dev->ethtool_ops->get_rxfh_context(dev, indir, hkey,
1213*4882a593Smuzhiyun &dev_hfunc,
1214*4882a593Smuzhiyun rxfh.rss_context);
1215*4882a593Smuzhiyun else
1216*4882a593Smuzhiyun ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc);
1217*4882a593Smuzhiyun if (ret)
1218*4882a593Smuzhiyun goto out;
1219*4882a593Smuzhiyun
1220*4882a593Smuzhiyun if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, hfunc),
1221*4882a593Smuzhiyun &dev_hfunc, sizeof(rxfh.hfunc))) {
1222*4882a593Smuzhiyun ret = -EFAULT;
1223*4882a593Smuzhiyun } else if (copy_to_user(useraddr +
1224*4882a593Smuzhiyun offsetof(struct ethtool_rxfh, rss_config[0]),
1225*4882a593Smuzhiyun rss_config, total_size)) {
1226*4882a593Smuzhiyun ret = -EFAULT;
1227*4882a593Smuzhiyun }
1228*4882a593Smuzhiyun out:
1229*4882a593Smuzhiyun kfree(rss_config);
1230*4882a593Smuzhiyun
1231*4882a593Smuzhiyun return ret;
1232*4882a593Smuzhiyun }
1233*4882a593Smuzhiyun
ethtool_set_rxfh(struct net_device * dev,void __user * useraddr)1234*4882a593Smuzhiyun static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
1235*4882a593Smuzhiyun void __user *useraddr)
1236*4882a593Smuzhiyun {
1237*4882a593Smuzhiyun int ret;
1238*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1239*4882a593Smuzhiyun struct ethtool_rxnfc rx_rings;
1240*4882a593Smuzhiyun struct ethtool_rxfh rxfh;
1241*4882a593Smuzhiyun u32 dev_indir_size = 0, dev_key_size = 0, i;
1242*4882a593Smuzhiyun u32 *indir = NULL, indir_bytes = 0;
1243*4882a593Smuzhiyun u8 *hkey = NULL;
1244*4882a593Smuzhiyun u8 *rss_config;
1245*4882a593Smuzhiyun u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
1246*4882a593Smuzhiyun bool delete = false;
1247*4882a593Smuzhiyun
1248*4882a593Smuzhiyun if (!ops->get_rxnfc || !ops->set_rxfh)
1249*4882a593Smuzhiyun return -EOPNOTSUPP;
1250*4882a593Smuzhiyun
1251*4882a593Smuzhiyun if (ops->get_rxfh_indir_size)
1252*4882a593Smuzhiyun dev_indir_size = ops->get_rxfh_indir_size(dev);
1253*4882a593Smuzhiyun if (ops->get_rxfh_key_size)
1254*4882a593Smuzhiyun dev_key_size = ops->get_rxfh_key_size(dev);
1255*4882a593Smuzhiyun
1256*4882a593Smuzhiyun if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
1257*4882a593Smuzhiyun return -EFAULT;
1258*4882a593Smuzhiyun
1259*4882a593Smuzhiyun /* Check that reserved fields are 0 for now */
1260*4882a593Smuzhiyun if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32)
1261*4882a593Smuzhiyun return -EINVAL;
1262*4882a593Smuzhiyun /* Most drivers don't handle rss_context, check it's 0 as well */
1263*4882a593Smuzhiyun if (rxfh.rss_context && !ops->set_rxfh_context)
1264*4882a593Smuzhiyun return -EOPNOTSUPP;
1265*4882a593Smuzhiyun
1266*4882a593Smuzhiyun /* If either indir, hash key or function is valid, proceed further.
1267*4882a593Smuzhiyun * Must request at least one change: indir size, hash key or function.
1268*4882a593Smuzhiyun */
1269*4882a593Smuzhiyun if ((rxfh.indir_size &&
1270*4882a593Smuzhiyun rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
1271*4882a593Smuzhiyun rxfh.indir_size != dev_indir_size) ||
1272*4882a593Smuzhiyun (rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
1273*4882a593Smuzhiyun (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
1274*4882a593Smuzhiyun rxfh.key_size == 0 && rxfh.hfunc == ETH_RSS_HASH_NO_CHANGE))
1275*4882a593Smuzhiyun return -EINVAL;
1276*4882a593Smuzhiyun
1277*4882a593Smuzhiyun if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
1278*4882a593Smuzhiyun indir_bytes = dev_indir_size * sizeof(indir[0]);
1279*4882a593Smuzhiyun
1280*4882a593Smuzhiyun rss_config = kzalloc(indir_bytes + rxfh.key_size, GFP_USER);
1281*4882a593Smuzhiyun if (!rss_config)
1282*4882a593Smuzhiyun return -ENOMEM;
1283*4882a593Smuzhiyun
1284*4882a593Smuzhiyun rx_rings.cmd = ETHTOOL_GRXRINGS;
1285*4882a593Smuzhiyun ret = ops->get_rxnfc(dev, &rx_rings, NULL);
1286*4882a593Smuzhiyun if (ret)
1287*4882a593Smuzhiyun goto out;
1288*4882a593Smuzhiyun
1289*4882a593Smuzhiyun /* rxfh.indir_size == 0 means reset the indir table to default (master
1290*4882a593Smuzhiyun * context) or delete the context (other RSS contexts).
1291*4882a593Smuzhiyun * rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged.
1292*4882a593Smuzhiyun */
1293*4882a593Smuzhiyun if (rxfh.indir_size &&
1294*4882a593Smuzhiyun rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) {
1295*4882a593Smuzhiyun indir = (u32 *)rss_config;
1296*4882a593Smuzhiyun ret = ethtool_copy_validate_indir(indir,
1297*4882a593Smuzhiyun useraddr + rss_cfg_offset,
1298*4882a593Smuzhiyun &rx_rings,
1299*4882a593Smuzhiyun rxfh.indir_size);
1300*4882a593Smuzhiyun if (ret)
1301*4882a593Smuzhiyun goto out;
1302*4882a593Smuzhiyun } else if (rxfh.indir_size == 0) {
1303*4882a593Smuzhiyun if (rxfh.rss_context == 0) {
1304*4882a593Smuzhiyun indir = (u32 *)rss_config;
1305*4882a593Smuzhiyun for (i = 0; i < dev_indir_size; i++)
1306*4882a593Smuzhiyun indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
1307*4882a593Smuzhiyun } else {
1308*4882a593Smuzhiyun delete = true;
1309*4882a593Smuzhiyun }
1310*4882a593Smuzhiyun }
1311*4882a593Smuzhiyun
1312*4882a593Smuzhiyun if (rxfh.key_size) {
1313*4882a593Smuzhiyun hkey = rss_config + indir_bytes;
1314*4882a593Smuzhiyun if (copy_from_user(hkey,
1315*4882a593Smuzhiyun useraddr + rss_cfg_offset + indir_bytes,
1316*4882a593Smuzhiyun rxfh.key_size)) {
1317*4882a593Smuzhiyun ret = -EFAULT;
1318*4882a593Smuzhiyun goto out;
1319*4882a593Smuzhiyun }
1320*4882a593Smuzhiyun }
1321*4882a593Smuzhiyun
1322*4882a593Smuzhiyun if (rxfh.rss_context)
1323*4882a593Smuzhiyun ret = ops->set_rxfh_context(dev, indir, hkey, rxfh.hfunc,
1324*4882a593Smuzhiyun &rxfh.rss_context, delete);
1325*4882a593Smuzhiyun else
1326*4882a593Smuzhiyun ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
1327*4882a593Smuzhiyun if (ret)
1328*4882a593Smuzhiyun goto out;
1329*4882a593Smuzhiyun
1330*4882a593Smuzhiyun if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context),
1331*4882a593Smuzhiyun &rxfh.rss_context, sizeof(rxfh.rss_context)))
1332*4882a593Smuzhiyun ret = -EFAULT;
1333*4882a593Smuzhiyun
1334*4882a593Smuzhiyun if (!rxfh.rss_context) {
1335*4882a593Smuzhiyun /* indicate whether rxfh was set to default */
1336*4882a593Smuzhiyun if (rxfh.indir_size == 0)
1337*4882a593Smuzhiyun dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
1338*4882a593Smuzhiyun else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
1339*4882a593Smuzhiyun dev->priv_flags |= IFF_RXFH_CONFIGURED;
1340*4882a593Smuzhiyun }
1341*4882a593Smuzhiyun
1342*4882a593Smuzhiyun out:
1343*4882a593Smuzhiyun kfree(rss_config);
1344*4882a593Smuzhiyun return ret;
1345*4882a593Smuzhiyun }
1346*4882a593Smuzhiyun
ethtool_get_regs(struct net_device * dev,char __user * useraddr)1347*4882a593Smuzhiyun static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
1348*4882a593Smuzhiyun {
1349*4882a593Smuzhiyun struct ethtool_regs regs;
1350*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1351*4882a593Smuzhiyun void *regbuf;
1352*4882a593Smuzhiyun int reglen, ret;
1353*4882a593Smuzhiyun
1354*4882a593Smuzhiyun if (!ops->get_regs || !ops->get_regs_len)
1355*4882a593Smuzhiyun return -EOPNOTSUPP;
1356*4882a593Smuzhiyun
1357*4882a593Smuzhiyun if (copy_from_user(®s, useraddr, sizeof(regs)))
1358*4882a593Smuzhiyun return -EFAULT;
1359*4882a593Smuzhiyun
1360*4882a593Smuzhiyun reglen = ops->get_regs_len(dev);
1361*4882a593Smuzhiyun if (reglen <= 0)
1362*4882a593Smuzhiyun return reglen;
1363*4882a593Smuzhiyun
1364*4882a593Smuzhiyun if (regs.len > reglen)
1365*4882a593Smuzhiyun regs.len = reglen;
1366*4882a593Smuzhiyun
1367*4882a593Smuzhiyun regbuf = vzalloc(reglen);
1368*4882a593Smuzhiyun if (!regbuf)
1369*4882a593Smuzhiyun return -ENOMEM;
1370*4882a593Smuzhiyun
1371*4882a593Smuzhiyun if (regs.len < reglen)
1372*4882a593Smuzhiyun reglen = regs.len;
1373*4882a593Smuzhiyun
1374*4882a593Smuzhiyun ops->get_regs(dev, ®s, regbuf);
1375*4882a593Smuzhiyun
1376*4882a593Smuzhiyun ret = -EFAULT;
1377*4882a593Smuzhiyun if (copy_to_user(useraddr, ®s, sizeof(regs)))
1378*4882a593Smuzhiyun goto out;
1379*4882a593Smuzhiyun useraddr += offsetof(struct ethtool_regs, data);
1380*4882a593Smuzhiyun if (copy_to_user(useraddr, regbuf, reglen))
1381*4882a593Smuzhiyun goto out;
1382*4882a593Smuzhiyun ret = 0;
1383*4882a593Smuzhiyun
1384*4882a593Smuzhiyun out:
1385*4882a593Smuzhiyun vfree(regbuf);
1386*4882a593Smuzhiyun return ret;
1387*4882a593Smuzhiyun }
1388*4882a593Smuzhiyun
ethtool_reset(struct net_device * dev,char __user * useraddr)1389*4882a593Smuzhiyun static int ethtool_reset(struct net_device *dev, char __user *useraddr)
1390*4882a593Smuzhiyun {
1391*4882a593Smuzhiyun struct ethtool_value reset;
1392*4882a593Smuzhiyun int ret;
1393*4882a593Smuzhiyun
1394*4882a593Smuzhiyun if (!dev->ethtool_ops->reset)
1395*4882a593Smuzhiyun return -EOPNOTSUPP;
1396*4882a593Smuzhiyun
1397*4882a593Smuzhiyun if (copy_from_user(&reset, useraddr, sizeof(reset)))
1398*4882a593Smuzhiyun return -EFAULT;
1399*4882a593Smuzhiyun
1400*4882a593Smuzhiyun ret = dev->ethtool_ops->reset(dev, &reset.data);
1401*4882a593Smuzhiyun if (ret)
1402*4882a593Smuzhiyun return ret;
1403*4882a593Smuzhiyun
1404*4882a593Smuzhiyun if (copy_to_user(useraddr, &reset, sizeof(reset)))
1405*4882a593Smuzhiyun return -EFAULT;
1406*4882a593Smuzhiyun return 0;
1407*4882a593Smuzhiyun }
1408*4882a593Smuzhiyun
ethtool_get_wol(struct net_device * dev,char __user * useraddr)1409*4882a593Smuzhiyun static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
1410*4882a593Smuzhiyun {
1411*4882a593Smuzhiyun struct ethtool_wolinfo wol;
1412*4882a593Smuzhiyun
1413*4882a593Smuzhiyun if (!dev->ethtool_ops->get_wol)
1414*4882a593Smuzhiyun return -EOPNOTSUPP;
1415*4882a593Smuzhiyun
1416*4882a593Smuzhiyun memset(&wol, 0, sizeof(struct ethtool_wolinfo));
1417*4882a593Smuzhiyun wol.cmd = ETHTOOL_GWOL;
1418*4882a593Smuzhiyun dev->ethtool_ops->get_wol(dev, &wol);
1419*4882a593Smuzhiyun
1420*4882a593Smuzhiyun if (copy_to_user(useraddr, &wol, sizeof(wol)))
1421*4882a593Smuzhiyun return -EFAULT;
1422*4882a593Smuzhiyun return 0;
1423*4882a593Smuzhiyun }
1424*4882a593Smuzhiyun
ethtool_set_wol(struct net_device * dev,char __user * useraddr)1425*4882a593Smuzhiyun static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
1426*4882a593Smuzhiyun {
1427*4882a593Smuzhiyun struct ethtool_wolinfo wol;
1428*4882a593Smuzhiyun int ret;
1429*4882a593Smuzhiyun
1430*4882a593Smuzhiyun if (!dev->ethtool_ops->set_wol)
1431*4882a593Smuzhiyun return -EOPNOTSUPP;
1432*4882a593Smuzhiyun
1433*4882a593Smuzhiyun if (copy_from_user(&wol, useraddr, sizeof(wol)))
1434*4882a593Smuzhiyun return -EFAULT;
1435*4882a593Smuzhiyun
1436*4882a593Smuzhiyun ret = dev->ethtool_ops->set_wol(dev, &wol);
1437*4882a593Smuzhiyun if (ret)
1438*4882a593Smuzhiyun return ret;
1439*4882a593Smuzhiyun
1440*4882a593Smuzhiyun dev->wol_enabled = !!wol.wolopts;
1441*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL);
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun return 0;
1444*4882a593Smuzhiyun }
1445*4882a593Smuzhiyun
ethtool_get_eee(struct net_device * dev,char __user * useraddr)1446*4882a593Smuzhiyun static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)
1447*4882a593Smuzhiyun {
1448*4882a593Smuzhiyun struct ethtool_eee edata;
1449*4882a593Smuzhiyun int rc;
1450*4882a593Smuzhiyun
1451*4882a593Smuzhiyun if (!dev->ethtool_ops->get_eee)
1452*4882a593Smuzhiyun return -EOPNOTSUPP;
1453*4882a593Smuzhiyun
1454*4882a593Smuzhiyun memset(&edata, 0, sizeof(struct ethtool_eee));
1455*4882a593Smuzhiyun edata.cmd = ETHTOOL_GEEE;
1456*4882a593Smuzhiyun rc = dev->ethtool_ops->get_eee(dev, &edata);
1457*4882a593Smuzhiyun
1458*4882a593Smuzhiyun if (rc)
1459*4882a593Smuzhiyun return rc;
1460*4882a593Smuzhiyun
1461*4882a593Smuzhiyun if (copy_to_user(useraddr, &edata, sizeof(edata)))
1462*4882a593Smuzhiyun return -EFAULT;
1463*4882a593Smuzhiyun
1464*4882a593Smuzhiyun return 0;
1465*4882a593Smuzhiyun }
1466*4882a593Smuzhiyun
ethtool_set_eee(struct net_device * dev,char __user * useraddr)1467*4882a593Smuzhiyun static int ethtool_set_eee(struct net_device *dev, char __user *useraddr)
1468*4882a593Smuzhiyun {
1469*4882a593Smuzhiyun struct ethtool_eee edata;
1470*4882a593Smuzhiyun int ret;
1471*4882a593Smuzhiyun
1472*4882a593Smuzhiyun if (!dev->ethtool_ops->set_eee)
1473*4882a593Smuzhiyun return -EOPNOTSUPP;
1474*4882a593Smuzhiyun
1475*4882a593Smuzhiyun if (copy_from_user(&edata, useraddr, sizeof(edata)))
1476*4882a593Smuzhiyun return -EFAULT;
1477*4882a593Smuzhiyun
1478*4882a593Smuzhiyun ret = dev->ethtool_ops->set_eee(dev, &edata);
1479*4882a593Smuzhiyun if (!ret)
1480*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL);
1481*4882a593Smuzhiyun return ret;
1482*4882a593Smuzhiyun }
1483*4882a593Smuzhiyun
ethtool_nway_reset(struct net_device * dev)1484*4882a593Smuzhiyun static int ethtool_nway_reset(struct net_device *dev)
1485*4882a593Smuzhiyun {
1486*4882a593Smuzhiyun if (!dev->ethtool_ops->nway_reset)
1487*4882a593Smuzhiyun return -EOPNOTSUPP;
1488*4882a593Smuzhiyun
1489*4882a593Smuzhiyun return dev->ethtool_ops->nway_reset(dev);
1490*4882a593Smuzhiyun }
1491*4882a593Smuzhiyun
ethtool_get_link(struct net_device * dev,char __user * useraddr)1492*4882a593Smuzhiyun static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
1493*4882a593Smuzhiyun {
1494*4882a593Smuzhiyun struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
1495*4882a593Smuzhiyun int link = __ethtool_get_link(dev);
1496*4882a593Smuzhiyun
1497*4882a593Smuzhiyun if (link < 0)
1498*4882a593Smuzhiyun return link;
1499*4882a593Smuzhiyun
1500*4882a593Smuzhiyun edata.data = link;
1501*4882a593Smuzhiyun if (copy_to_user(useraddr, &edata, sizeof(edata)))
1502*4882a593Smuzhiyun return -EFAULT;
1503*4882a593Smuzhiyun return 0;
1504*4882a593Smuzhiyun }
1505*4882a593Smuzhiyun
ethtool_get_any_eeprom(struct net_device * dev,void __user * useraddr,int (* getter)(struct net_device *,struct ethtool_eeprom *,u8 *),u32 total_len)1506*4882a593Smuzhiyun static int ethtool_get_any_eeprom(struct net_device *dev, void __user *useraddr,
1507*4882a593Smuzhiyun int (*getter)(struct net_device *,
1508*4882a593Smuzhiyun struct ethtool_eeprom *, u8 *),
1509*4882a593Smuzhiyun u32 total_len)
1510*4882a593Smuzhiyun {
1511*4882a593Smuzhiyun struct ethtool_eeprom eeprom;
1512*4882a593Smuzhiyun void __user *userbuf = useraddr + sizeof(eeprom);
1513*4882a593Smuzhiyun u32 bytes_remaining;
1514*4882a593Smuzhiyun u8 *data;
1515*4882a593Smuzhiyun int ret = 0;
1516*4882a593Smuzhiyun
1517*4882a593Smuzhiyun if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
1518*4882a593Smuzhiyun return -EFAULT;
1519*4882a593Smuzhiyun
1520*4882a593Smuzhiyun /* Check for wrap and zero */
1521*4882a593Smuzhiyun if (eeprom.offset + eeprom.len <= eeprom.offset)
1522*4882a593Smuzhiyun return -EINVAL;
1523*4882a593Smuzhiyun
1524*4882a593Smuzhiyun /* Check for exceeding total eeprom len */
1525*4882a593Smuzhiyun if (eeprom.offset + eeprom.len > total_len)
1526*4882a593Smuzhiyun return -EINVAL;
1527*4882a593Smuzhiyun
1528*4882a593Smuzhiyun data = kzalloc(PAGE_SIZE, GFP_USER);
1529*4882a593Smuzhiyun if (!data)
1530*4882a593Smuzhiyun return -ENOMEM;
1531*4882a593Smuzhiyun
1532*4882a593Smuzhiyun bytes_remaining = eeprom.len;
1533*4882a593Smuzhiyun while (bytes_remaining > 0) {
1534*4882a593Smuzhiyun eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
1535*4882a593Smuzhiyun
1536*4882a593Smuzhiyun ret = getter(dev, &eeprom, data);
1537*4882a593Smuzhiyun if (ret)
1538*4882a593Smuzhiyun break;
1539*4882a593Smuzhiyun if (copy_to_user(userbuf, data, eeprom.len)) {
1540*4882a593Smuzhiyun ret = -EFAULT;
1541*4882a593Smuzhiyun break;
1542*4882a593Smuzhiyun }
1543*4882a593Smuzhiyun userbuf += eeprom.len;
1544*4882a593Smuzhiyun eeprom.offset += eeprom.len;
1545*4882a593Smuzhiyun bytes_remaining -= eeprom.len;
1546*4882a593Smuzhiyun }
1547*4882a593Smuzhiyun
1548*4882a593Smuzhiyun eeprom.len = userbuf - (useraddr + sizeof(eeprom));
1549*4882a593Smuzhiyun eeprom.offset -= eeprom.len;
1550*4882a593Smuzhiyun if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
1551*4882a593Smuzhiyun ret = -EFAULT;
1552*4882a593Smuzhiyun
1553*4882a593Smuzhiyun kfree(data);
1554*4882a593Smuzhiyun return ret;
1555*4882a593Smuzhiyun }
1556*4882a593Smuzhiyun
ethtool_get_eeprom(struct net_device * dev,void __user * useraddr)1557*4882a593Smuzhiyun static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
1558*4882a593Smuzhiyun {
1559*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1560*4882a593Smuzhiyun
1561*4882a593Smuzhiyun if (!ops->get_eeprom || !ops->get_eeprom_len ||
1562*4882a593Smuzhiyun !ops->get_eeprom_len(dev))
1563*4882a593Smuzhiyun return -EOPNOTSUPP;
1564*4882a593Smuzhiyun
1565*4882a593Smuzhiyun return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom,
1566*4882a593Smuzhiyun ops->get_eeprom_len(dev));
1567*4882a593Smuzhiyun }
1568*4882a593Smuzhiyun
ethtool_set_eeprom(struct net_device * dev,void __user * useraddr)1569*4882a593Smuzhiyun static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
1570*4882a593Smuzhiyun {
1571*4882a593Smuzhiyun struct ethtool_eeprom eeprom;
1572*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1573*4882a593Smuzhiyun void __user *userbuf = useraddr + sizeof(eeprom);
1574*4882a593Smuzhiyun u32 bytes_remaining;
1575*4882a593Smuzhiyun u8 *data;
1576*4882a593Smuzhiyun int ret = 0;
1577*4882a593Smuzhiyun
1578*4882a593Smuzhiyun if (!ops->set_eeprom || !ops->get_eeprom_len ||
1579*4882a593Smuzhiyun !ops->get_eeprom_len(dev))
1580*4882a593Smuzhiyun return -EOPNOTSUPP;
1581*4882a593Smuzhiyun
1582*4882a593Smuzhiyun if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
1583*4882a593Smuzhiyun return -EFAULT;
1584*4882a593Smuzhiyun
1585*4882a593Smuzhiyun /* Check for wrap and zero */
1586*4882a593Smuzhiyun if (eeprom.offset + eeprom.len <= eeprom.offset)
1587*4882a593Smuzhiyun return -EINVAL;
1588*4882a593Smuzhiyun
1589*4882a593Smuzhiyun /* Check for exceeding total eeprom len */
1590*4882a593Smuzhiyun if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
1591*4882a593Smuzhiyun return -EINVAL;
1592*4882a593Smuzhiyun
1593*4882a593Smuzhiyun data = kzalloc(PAGE_SIZE, GFP_USER);
1594*4882a593Smuzhiyun if (!data)
1595*4882a593Smuzhiyun return -ENOMEM;
1596*4882a593Smuzhiyun
1597*4882a593Smuzhiyun bytes_remaining = eeprom.len;
1598*4882a593Smuzhiyun while (bytes_remaining > 0) {
1599*4882a593Smuzhiyun eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
1600*4882a593Smuzhiyun
1601*4882a593Smuzhiyun if (copy_from_user(data, userbuf, eeprom.len)) {
1602*4882a593Smuzhiyun ret = -EFAULT;
1603*4882a593Smuzhiyun break;
1604*4882a593Smuzhiyun }
1605*4882a593Smuzhiyun ret = ops->set_eeprom(dev, &eeprom, data);
1606*4882a593Smuzhiyun if (ret)
1607*4882a593Smuzhiyun break;
1608*4882a593Smuzhiyun userbuf += eeprom.len;
1609*4882a593Smuzhiyun eeprom.offset += eeprom.len;
1610*4882a593Smuzhiyun bytes_remaining -= eeprom.len;
1611*4882a593Smuzhiyun }
1612*4882a593Smuzhiyun
1613*4882a593Smuzhiyun kfree(data);
1614*4882a593Smuzhiyun return ret;
1615*4882a593Smuzhiyun }
1616*4882a593Smuzhiyun
ethtool_get_coalesce(struct net_device * dev,void __user * useraddr)1617*4882a593Smuzhiyun static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev,
1618*4882a593Smuzhiyun void __user *useraddr)
1619*4882a593Smuzhiyun {
1620*4882a593Smuzhiyun struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
1621*4882a593Smuzhiyun int ret;
1622*4882a593Smuzhiyun
1623*4882a593Smuzhiyun if (!dev->ethtool_ops->get_coalesce)
1624*4882a593Smuzhiyun return -EOPNOTSUPP;
1625*4882a593Smuzhiyun
1626*4882a593Smuzhiyun ret = dev->ethtool_ops->get_coalesce(dev, &coalesce);
1627*4882a593Smuzhiyun if (ret)
1628*4882a593Smuzhiyun return ret;
1629*4882a593Smuzhiyun
1630*4882a593Smuzhiyun if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
1631*4882a593Smuzhiyun return -EFAULT;
1632*4882a593Smuzhiyun return 0;
1633*4882a593Smuzhiyun }
1634*4882a593Smuzhiyun
1635*4882a593Smuzhiyun static bool
ethtool_set_coalesce_supported(struct net_device * dev,struct ethtool_coalesce * coalesce)1636*4882a593Smuzhiyun ethtool_set_coalesce_supported(struct net_device *dev,
1637*4882a593Smuzhiyun struct ethtool_coalesce *coalesce)
1638*4882a593Smuzhiyun {
1639*4882a593Smuzhiyun u32 supported_params = dev->ethtool_ops->supported_coalesce_params;
1640*4882a593Smuzhiyun u32 nonzero_params = 0;
1641*4882a593Smuzhiyun
1642*4882a593Smuzhiyun if (coalesce->rx_coalesce_usecs)
1643*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RX_USECS;
1644*4882a593Smuzhiyun if (coalesce->rx_max_coalesced_frames)
1645*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES;
1646*4882a593Smuzhiyun if (coalesce->rx_coalesce_usecs_irq)
1647*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RX_USECS_IRQ;
1648*4882a593Smuzhiyun if (coalesce->rx_max_coalesced_frames_irq)
1649*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ;
1650*4882a593Smuzhiyun if (coalesce->tx_coalesce_usecs)
1651*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_TX_USECS;
1652*4882a593Smuzhiyun if (coalesce->tx_max_coalesced_frames)
1653*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES;
1654*4882a593Smuzhiyun if (coalesce->tx_coalesce_usecs_irq)
1655*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_TX_USECS_IRQ;
1656*4882a593Smuzhiyun if (coalesce->tx_max_coalesced_frames_irq)
1657*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ;
1658*4882a593Smuzhiyun if (coalesce->stats_block_coalesce_usecs)
1659*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_STATS_BLOCK_USECS;
1660*4882a593Smuzhiyun if (coalesce->use_adaptive_rx_coalesce)
1661*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_RX;
1662*4882a593Smuzhiyun if (coalesce->use_adaptive_tx_coalesce)
1663*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_TX;
1664*4882a593Smuzhiyun if (coalesce->pkt_rate_low)
1665*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_LOW;
1666*4882a593Smuzhiyun if (coalesce->rx_coalesce_usecs_low)
1667*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RX_USECS_LOW;
1668*4882a593Smuzhiyun if (coalesce->rx_max_coalesced_frames_low)
1669*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW;
1670*4882a593Smuzhiyun if (coalesce->tx_coalesce_usecs_low)
1671*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_TX_USECS_LOW;
1672*4882a593Smuzhiyun if (coalesce->tx_max_coalesced_frames_low)
1673*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW;
1674*4882a593Smuzhiyun if (coalesce->pkt_rate_high)
1675*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_HIGH;
1676*4882a593Smuzhiyun if (coalesce->rx_coalesce_usecs_high)
1677*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RX_USECS_HIGH;
1678*4882a593Smuzhiyun if (coalesce->rx_max_coalesced_frames_high)
1679*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH;
1680*4882a593Smuzhiyun if (coalesce->tx_coalesce_usecs_high)
1681*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_TX_USECS_HIGH;
1682*4882a593Smuzhiyun if (coalesce->tx_max_coalesced_frames_high)
1683*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH;
1684*4882a593Smuzhiyun if (coalesce->rate_sample_interval)
1685*4882a593Smuzhiyun nonzero_params |= ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL;
1686*4882a593Smuzhiyun
1687*4882a593Smuzhiyun return (supported_params & nonzero_params) == nonzero_params;
1688*4882a593Smuzhiyun }
1689*4882a593Smuzhiyun
ethtool_set_coalesce(struct net_device * dev,void __user * useraddr)1690*4882a593Smuzhiyun static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
1691*4882a593Smuzhiyun void __user *useraddr)
1692*4882a593Smuzhiyun {
1693*4882a593Smuzhiyun struct ethtool_coalesce coalesce;
1694*4882a593Smuzhiyun int ret;
1695*4882a593Smuzhiyun
1696*4882a593Smuzhiyun if (!dev->ethtool_ops->set_coalesce)
1697*4882a593Smuzhiyun return -EOPNOTSUPP;
1698*4882a593Smuzhiyun
1699*4882a593Smuzhiyun if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
1700*4882a593Smuzhiyun return -EFAULT;
1701*4882a593Smuzhiyun
1702*4882a593Smuzhiyun if (!ethtool_set_coalesce_supported(dev, &coalesce))
1703*4882a593Smuzhiyun return -EOPNOTSUPP;
1704*4882a593Smuzhiyun
1705*4882a593Smuzhiyun ret = dev->ethtool_ops->set_coalesce(dev, &coalesce);
1706*4882a593Smuzhiyun if (!ret)
1707*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL);
1708*4882a593Smuzhiyun return ret;
1709*4882a593Smuzhiyun }
1710*4882a593Smuzhiyun
ethtool_get_ringparam(struct net_device * dev,void __user * useraddr)1711*4882a593Smuzhiyun static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
1712*4882a593Smuzhiyun {
1713*4882a593Smuzhiyun struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM };
1714*4882a593Smuzhiyun
1715*4882a593Smuzhiyun if (!dev->ethtool_ops->get_ringparam)
1716*4882a593Smuzhiyun return -EOPNOTSUPP;
1717*4882a593Smuzhiyun
1718*4882a593Smuzhiyun dev->ethtool_ops->get_ringparam(dev, &ringparam);
1719*4882a593Smuzhiyun
1720*4882a593Smuzhiyun if (copy_to_user(useraddr, &ringparam, sizeof(ringparam)))
1721*4882a593Smuzhiyun return -EFAULT;
1722*4882a593Smuzhiyun return 0;
1723*4882a593Smuzhiyun }
1724*4882a593Smuzhiyun
ethtool_set_ringparam(struct net_device * dev,void __user * useraddr)1725*4882a593Smuzhiyun static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
1726*4882a593Smuzhiyun {
1727*4882a593Smuzhiyun struct ethtool_ringparam ringparam, max = { .cmd = ETHTOOL_GRINGPARAM };
1728*4882a593Smuzhiyun int ret;
1729*4882a593Smuzhiyun
1730*4882a593Smuzhiyun if (!dev->ethtool_ops->set_ringparam || !dev->ethtool_ops->get_ringparam)
1731*4882a593Smuzhiyun return -EOPNOTSUPP;
1732*4882a593Smuzhiyun
1733*4882a593Smuzhiyun if (copy_from_user(&ringparam, useraddr, sizeof(ringparam)))
1734*4882a593Smuzhiyun return -EFAULT;
1735*4882a593Smuzhiyun
1736*4882a593Smuzhiyun dev->ethtool_ops->get_ringparam(dev, &max);
1737*4882a593Smuzhiyun
1738*4882a593Smuzhiyun /* ensure new ring parameters are within the maximums */
1739*4882a593Smuzhiyun if (ringparam.rx_pending > max.rx_max_pending ||
1740*4882a593Smuzhiyun ringparam.rx_mini_pending > max.rx_mini_max_pending ||
1741*4882a593Smuzhiyun ringparam.rx_jumbo_pending > max.rx_jumbo_max_pending ||
1742*4882a593Smuzhiyun ringparam.tx_pending > max.tx_max_pending)
1743*4882a593Smuzhiyun return -EINVAL;
1744*4882a593Smuzhiyun
1745*4882a593Smuzhiyun ret = dev->ethtool_ops->set_ringparam(dev, &ringparam);
1746*4882a593Smuzhiyun if (!ret)
1747*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL);
1748*4882a593Smuzhiyun return ret;
1749*4882a593Smuzhiyun }
1750*4882a593Smuzhiyun
ethtool_get_channels(struct net_device * dev,void __user * useraddr)1751*4882a593Smuzhiyun static noinline_for_stack int ethtool_get_channels(struct net_device *dev,
1752*4882a593Smuzhiyun void __user *useraddr)
1753*4882a593Smuzhiyun {
1754*4882a593Smuzhiyun struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS };
1755*4882a593Smuzhiyun
1756*4882a593Smuzhiyun if (!dev->ethtool_ops->get_channels)
1757*4882a593Smuzhiyun return -EOPNOTSUPP;
1758*4882a593Smuzhiyun
1759*4882a593Smuzhiyun dev->ethtool_ops->get_channels(dev, &channels);
1760*4882a593Smuzhiyun
1761*4882a593Smuzhiyun if (copy_to_user(useraddr, &channels, sizeof(channels)))
1762*4882a593Smuzhiyun return -EFAULT;
1763*4882a593Smuzhiyun return 0;
1764*4882a593Smuzhiyun }
1765*4882a593Smuzhiyun
ethtool_set_channels(struct net_device * dev,void __user * useraddr)1766*4882a593Smuzhiyun static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
1767*4882a593Smuzhiyun void __user *useraddr)
1768*4882a593Smuzhiyun {
1769*4882a593Smuzhiyun struct ethtool_channels channels, curr = { .cmd = ETHTOOL_GCHANNELS };
1770*4882a593Smuzhiyun u16 from_channel, to_channel;
1771*4882a593Smuzhiyun u32 max_rx_in_use = 0;
1772*4882a593Smuzhiyun unsigned int i;
1773*4882a593Smuzhiyun int ret;
1774*4882a593Smuzhiyun
1775*4882a593Smuzhiyun if (!dev->ethtool_ops->set_channels || !dev->ethtool_ops->get_channels)
1776*4882a593Smuzhiyun return -EOPNOTSUPP;
1777*4882a593Smuzhiyun
1778*4882a593Smuzhiyun if (copy_from_user(&channels, useraddr, sizeof(channels)))
1779*4882a593Smuzhiyun return -EFAULT;
1780*4882a593Smuzhiyun
1781*4882a593Smuzhiyun dev->ethtool_ops->get_channels(dev, &curr);
1782*4882a593Smuzhiyun
1783*4882a593Smuzhiyun if (channels.rx_count == curr.rx_count &&
1784*4882a593Smuzhiyun channels.tx_count == curr.tx_count &&
1785*4882a593Smuzhiyun channels.combined_count == curr.combined_count &&
1786*4882a593Smuzhiyun channels.other_count == curr.other_count)
1787*4882a593Smuzhiyun return 0;
1788*4882a593Smuzhiyun
1789*4882a593Smuzhiyun /* ensure new counts are within the maximums */
1790*4882a593Smuzhiyun if (channels.rx_count > curr.max_rx ||
1791*4882a593Smuzhiyun channels.tx_count > curr.max_tx ||
1792*4882a593Smuzhiyun channels.combined_count > curr.max_combined ||
1793*4882a593Smuzhiyun channels.other_count > curr.max_other)
1794*4882a593Smuzhiyun return -EINVAL;
1795*4882a593Smuzhiyun
1796*4882a593Smuzhiyun /* ensure there is at least one RX and one TX channel */
1797*4882a593Smuzhiyun if (!channels.combined_count &&
1798*4882a593Smuzhiyun (!channels.rx_count || !channels.tx_count))
1799*4882a593Smuzhiyun return -EINVAL;
1800*4882a593Smuzhiyun
1801*4882a593Smuzhiyun /* ensure the new Rx count fits within the configured Rx flow
1802*4882a593Smuzhiyun * indirection table settings */
1803*4882a593Smuzhiyun if (netif_is_rxfh_configured(dev) &&
1804*4882a593Smuzhiyun !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) &&
1805*4882a593Smuzhiyun (channels.combined_count + channels.rx_count) <= max_rx_in_use)
1806*4882a593Smuzhiyun return -EINVAL;
1807*4882a593Smuzhiyun
1808*4882a593Smuzhiyun /* Disabling channels, query zero-copy AF_XDP sockets */
1809*4882a593Smuzhiyun from_channel = channels.combined_count +
1810*4882a593Smuzhiyun min(channels.rx_count, channels.tx_count);
1811*4882a593Smuzhiyun to_channel = curr.combined_count + max(curr.rx_count, curr.tx_count);
1812*4882a593Smuzhiyun for (i = from_channel; i < to_channel; i++)
1813*4882a593Smuzhiyun if (xsk_get_pool_from_qid(dev, i))
1814*4882a593Smuzhiyun return -EINVAL;
1815*4882a593Smuzhiyun
1816*4882a593Smuzhiyun ret = dev->ethtool_ops->set_channels(dev, &channels);
1817*4882a593Smuzhiyun if (!ret)
1818*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF, NULL);
1819*4882a593Smuzhiyun return ret;
1820*4882a593Smuzhiyun }
1821*4882a593Smuzhiyun
ethtool_get_pauseparam(struct net_device * dev,void __user * useraddr)1822*4882a593Smuzhiyun static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
1823*4882a593Smuzhiyun {
1824*4882a593Smuzhiyun struct ethtool_pauseparam pauseparam = { .cmd = ETHTOOL_GPAUSEPARAM };
1825*4882a593Smuzhiyun
1826*4882a593Smuzhiyun if (!dev->ethtool_ops->get_pauseparam)
1827*4882a593Smuzhiyun return -EOPNOTSUPP;
1828*4882a593Smuzhiyun
1829*4882a593Smuzhiyun dev->ethtool_ops->get_pauseparam(dev, &pauseparam);
1830*4882a593Smuzhiyun
1831*4882a593Smuzhiyun if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam)))
1832*4882a593Smuzhiyun return -EFAULT;
1833*4882a593Smuzhiyun return 0;
1834*4882a593Smuzhiyun }
1835*4882a593Smuzhiyun
ethtool_set_pauseparam(struct net_device * dev,void __user * useraddr)1836*4882a593Smuzhiyun static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
1837*4882a593Smuzhiyun {
1838*4882a593Smuzhiyun struct ethtool_pauseparam pauseparam;
1839*4882a593Smuzhiyun int ret;
1840*4882a593Smuzhiyun
1841*4882a593Smuzhiyun if (!dev->ethtool_ops->set_pauseparam)
1842*4882a593Smuzhiyun return -EOPNOTSUPP;
1843*4882a593Smuzhiyun
1844*4882a593Smuzhiyun if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))
1845*4882a593Smuzhiyun return -EFAULT;
1846*4882a593Smuzhiyun
1847*4882a593Smuzhiyun ret = dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
1848*4882a593Smuzhiyun if (!ret)
1849*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_PAUSE_NTF, NULL);
1850*4882a593Smuzhiyun return ret;
1851*4882a593Smuzhiyun }
1852*4882a593Smuzhiyun
ethtool_self_test(struct net_device * dev,char __user * useraddr)1853*4882a593Smuzhiyun static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
1854*4882a593Smuzhiyun {
1855*4882a593Smuzhiyun struct ethtool_test test;
1856*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1857*4882a593Smuzhiyun u64 *data;
1858*4882a593Smuzhiyun int ret, test_len;
1859*4882a593Smuzhiyun
1860*4882a593Smuzhiyun if (!ops->self_test || !ops->get_sset_count)
1861*4882a593Smuzhiyun return -EOPNOTSUPP;
1862*4882a593Smuzhiyun
1863*4882a593Smuzhiyun test_len = ops->get_sset_count(dev, ETH_SS_TEST);
1864*4882a593Smuzhiyun if (test_len < 0)
1865*4882a593Smuzhiyun return test_len;
1866*4882a593Smuzhiyun WARN_ON(test_len == 0);
1867*4882a593Smuzhiyun
1868*4882a593Smuzhiyun if (copy_from_user(&test, useraddr, sizeof(test)))
1869*4882a593Smuzhiyun return -EFAULT;
1870*4882a593Smuzhiyun
1871*4882a593Smuzhiyun test.len = test_len;
1872*4882a593Smuzhiyun data = kcalloc(test_len, sizeof(u64), GFP_USER);
1873*4882a593Smuzhiyun if (!data)
1874*4882a593Smuzhiyun return -ENOMEM;
1875*4882a593Smuzhiyun
1876*4882a593Smuzhiyun netif_testing_on(dev);
1877*4882a593Smuzhiyun ops->self_test(dev, &test, data);
1878*4882a593Smuzhiyun netif_testing_off(dev);
1879*4882a593Smuzhiyun
1880*4882a593Smuzhiyun ret = -EFAULT;
1881*4882a593Smuzhiyun if (copy_to_user(useraddr, &test, sizeof(test)))
1882*4882a593Smuzhiyun goto out;
1883*4882a593Smuzhiyun useraddr += sizeof(test);
1884*4882a593Smuzhiyun if (copy_to_user(useraddr, data, test.len * sizeof(u64)))
1885*4882a593Smuzhiyun goto out;
1886*4882a593Smuzhiyun ret = 0;
1887*4882a593Smuzhiyun
1888*4882a593Smuzhiyun out:
1889*4882a593Smuzhiyun kfree(data);
1890*4882a593Smuzhiyun return ret;
1891*4882a593Smuzhiyun }
1892*4882a593Smuzhiyun
ethtool_get_strings(struct net_device * dev,void __user * useraddr)1893*4882a593Smuzhiyun static int ethtool_get_strings(struct net_device *dev, void __user *useraddr)
1894*4882a593Smuzhiyun {
1895*4882a593Smuzhiyun struct ethtool_gstrings gstrings;
1896*4882a593Smuzhiyun u8 *data;
1897*4882a593Smuzhiyun int ret;
1898*4882a593Smuzhiyun
1899*4882a593Smuzhiyun if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
1900*4882a593Smuzhiyun return -EFAULT;
1901*4882a593Smuzhiyun
1902*4882a593Smuzhiyun ret = __ethtool_get_sset_count(dev, gstrings.string_set);
1903*4882a593Smuzhiyun if (ret < 0)
1904*4882a593Smuzhiyun return ret;
1905*4882a593Smuzhiyun if (ret > S32_MAX / ETH_GSTRING_LEN)
1906*4882a593Smuzhiyun return -ENOMEM;
1907*4882a593Smuzhiyun WARN_ON_ONCE(!ret);
1908*4882a593Smuzhiyun
1909*4882a593Smuzhiyun gstrings.len = ret;
1910*4882a593Smuzhiyun
1911*4882a593Smuzhiyun if (gstrings.len) {
1912*4882a593Smuzhiyun data = vzalloc(array_size(gstrings.len, ETH_GSTRING_LEN));
1913*4882a593Smuzhiyun if (!data)
1914*4882a593Smuzhiyun return -ENOMEM;
1915*4882a593Smuzhiyun
1916*4882a593Smuzhiyun __ethtool_get_strings(dev, gstrings.string_set, data);
1917*4882a593Smuzhiyun } else {
1918*4882a593Smuzhiyun data = NULL;
1919*4882a593Smuzhiyun }
1920*4882a593Smuzhiyun
1921*4882a593Smuzhiyun ret = -EFAULT;
1922*4882a593Smuzhiyun if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
1923*4882a593Smuzhiyun goto out;
1924*4882a593Smuzhiyun useraddr += sizeof(gstrings);
1925*4882a593Smuzhiyun if (gstrings.len &&
1926*4882a593Smuzhiyun copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
1927*4882a593Smuzhiyun goto out;
1928*4882a593Smuzhiyun ret = 0;
1929*4882a593Smuzhiyun
1930*4882a593Smuzhiyun out:
1931*4882a593Smuzhiyun vfree(data);
1932*4882a593Smuzhiyun return ret;
1933*4882a593Smuzhiyun }
1934*4882a593Smuzhiyun
ethtool_phys_id(struct net_device * dev,void __user * useraddr)1935*4882a593Smuzhiyun static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
1936*4882a593Smuzhiyun {
1937*4882a593Smuzhiyun struct ethtool_value id;
1938*4882a593Smuzhiyun static bool busy;
1939*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1940*4882a593Smuzhiyun int rc;
1941*4882a593Smuzhiyun
1942*4882a593Smuzhiyun if (!ops->set_phys_id)
1943*4882a593Smuzhiyun return -EOPNOTSUPP;
1944*4882a593Smuzhiyun
1945*4882a593Smuzhiyun if (busy)
1946*4882a593Smuzhiyun return -EBUSY;
1947*4882a593Smuzhiyun
1948*4882a593Smuzhiyun if (copy_from_user(&id, useraddr, sizeof(id)))
1949*4882a593Smuzhiyun return -EFAULT;
1950*4882a593Smuzhiyun
1951*4882a593Smuzhiyun rc = ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE);
1952*4882a593Smuzhiyun if (rc < 0)
1953*4882a593Smuzhiyun return rc;
1954*4882a593Smuzhiyun
1955*4882a593Smuzhiyun /* Drop the RTNL lock while waiting, but prevent reentry or
1956*4882a593Smuzhiyun * removal of the device.
1957*4882a593Smuzhiyun */
1958*4882a593Smuzhiyun busy = true;
1959*4882a593Smuzhiyun dev_hold(dev);
1960*4882a593Smuzhiyun rtnl_unlock();
1961*4882a593Smuzhiyun
1962*4882a593Smuzhiyun if (rc == 0) {
1963*4882a593Smuzhiyun /* Driver will handle this itself */
1964*4882a593Smuzhiyun schedule_timeout_interruptible(
1965*4882a593Smuzhiyun id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT);
1966*4882a593Smuzhiyun } else {
1967*4882a593Smuzhiyun /* Driver expects to be called at twice the frequency in rc */
1968*4882a593Smuzhiyun int n = rc * 2, interval = HZ / n;
1969*4882a593Smuzhiyun u64 count = n * id.data, i = 0;
1970*4882a593Smuzhiyun
1971*4882a593Smuzhiyun do {
1972*4882a593Smuzhiyun rtnl_lock();
1973*4882a593Smuzhiyun rc = ops->set_phys_id(dev,
1974*4882a593Smuzhiyun (i++ & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON);
1975*4882a593Smuzhiyun rtnl_unlock();
1976*4882a593Smuzhiyun if (rc)
1977*4882a593Smuzhiyun break;
1978*4882a593Smuzhiyun schedule_timeout_interruptible(interval);
1979*4882a593Smuzhiyun } while (!signal_pending(current) && (!id.data || i < count));
1980*4882a593Smuzhiyun }
1981*4882a593Smuzhiyun
1982*4882a593Smuzhiyun rtnl_lock();
1983*4882a593Smuzhiyun dev_put(dev);
1984*4882a593Smuzhiyun busy = false;
1985*4882a593Smuzhiyun
1986*4882a593Smuzhiyun (void) ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE);
1987*4882a593Smuzhiyun return rc;
1988*4882a593Smuzhiyun }
1989*4882a593Smuzhiyun
ethtool_get_stats(struct net_device * dev,void __user * useraddr)1990*4882a593Smuzhiyun static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
1991*4882a593Smuzhiyun {
1992*4882a593Smuzhiyun struct ethtool_stats stats;
1993*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
1994*4882a593Smuzhiyun u64 *data;
1995*4882a593Smuzhiyun int ret, n_stats;
1996*4882a593Smuzhiyun
1997*4882a593Smuzhiyun if (!ops->get_ethtool_stats || !ops->get_sset_count)
1998*4882a593Smuzhiyun return -EOPNOTSUPP;
1999*4882a593Smuzhiyun
2000*4882a593Smuzhiyun n_stats = ops->get_sset_count(dev, ETH_SS_STATS);
2001*4882a593Smuzhiyun if (n_stats < 0)
2002*4882a593Smuzhiyun return n_stats;
2003*4882a593Smuzhiyun if (n_stats > S32_MAX / sizeof(u64))
2004*4882a593Smuzhiyun return -ENOMEM;
2005*4882a593Smuzhiyun WARN_ON_ONCE(!n_stats);
2006*4882a593Smuzhiyun if (copy_from_user(&stats, useraddr, sizeof(stats)))
2007*4882a593Smuzhiyun return -EFAULT;
2008*4882a593Smuzhiyun
2009*4882a593Smuzhiyun stats.n_stats = n_stats;
2010*4882a593Smuzhiyun
2011*4882a593Smuzhiyun if (n_stats) {
2012*4882a593Smuzhiyun data = vzalloc(array_size(n_stats, sizeof(u64)));
2013*4882a593Smuzhiyun if (!data)
2014*4882a593Smuzhiyun return -ENOMEM;
2015*4882a593Smuzhiyun ops->get_ethtool_stats(dev, &stats, data);
2016*4882a593Smuzhiyun } else {
2017*4882a593Smuzhiyun data = NULL;
2018*4882a593Smuzhiyun }
2019*4882a593Smuzhiyun
2020*4882a593Smuzhiyun ret = -EFAULT;
2021*4882a593Smuzhiyun if (copy_to_user(useraddr, &stats, sizeof(stats)))
2022*4882a593Smuzhiyun goto out;
2023*4882a593Smuzhiyun useraddr += sizeof(stats);
2024*4882a593Smuzhiyun if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
2025*4882a593Smuzhiyun goto out;
2026*4882a593Smuzhiyun ret = 0;
2027*4882a593Smuzhiyun
2028*4882a593Smuzhiyun out:
2029*4882a593Smuzhiyun vfree(data);
2030*4882a593Smuzhiyun return ret;
2031*4882a593Smuzhiyun }
2032*4882a593Smuzhiyun
ethtool_get_phy_stats(struct net_device * dev,void __user * useraddr)2033*4882a593Smuzhiyun static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
2034*4882a593Smuzhiyun {
2035*4882a593Smuzhiyun const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
2036*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
2037*4882a593Smuzhiyun struct phy_device *phydev = dev->phydev;
2038*4882a593Smuzhiyun struct ethtool_stats stats;
2039*4882a593Smuzhiyun u64 *data;
2040*4882a593Smuzhiyun int ret, n_stats;
2041*4882a593Smuzhiyun
2042*4882a593Smuzhiyun if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
2043*4882a593Smuzhiyun return -EOPNOTSUPP;
2044*4882a593Smuzhiyun
2045*4882a593Smuzhiyun if (dev->phydev && !ops->get_ethtool_phy_stats &&
2046*4882a593Smuzhiyun phy_ops && phy_ops->get_sset_count)
2047*4882a593Smuzhiyun n_stats = phy_ops->get_sset_count(dev->phydev);
2048*4882a593Smuzhiyun else
2049*4882a593Smuzhiyun n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
2050*4882a593Smuzhiyun if (n_stats < 0)
2051*4882a593Smuzhiyun return n_stats;
2052*4882a593Smuzhiyun if (n_stats > S32_MAX / sizeof(u64))
2053*4882a593Smuzhiyun return -ENOMEM;
2054*4882a593Smuzhiyun WARN_ON_ONCE(!n_stats);
2055*4882a593Smuzhiyun
2056*4882a593Smuzhiyun if (copy_from_user(&stats, useraddr, sizeof(stats)))
2057*4882a593Smuzhiyun return -EFAULT;
2058*4882a593Smuzhiyun
2059*4882a593Smuzhiyun stats.n_stats = n_stats;
2060*4882a593Smuzhiyun
2061*4882a593Smuzhiyun if (n_stats) {
2062*4882a593Smuzhiyun data = vzalloc(array_size(n_stats, sizeof(u64)));
2063*4882a593Smuzhiyun if (!data)
2064*4882a593Smuzhiyun return -ENOMEM;
2065*4882a593Smuzhiyun
2066*4882a593Smuzhiyun if (dev->phydev && !ops->get_ethtool_phy_stats &&
2067*4882a593Smuzhiyun phy_ops && phy_ops->get_stats) {
2068*4882a593Smuzhiyun ret = phy_ops->get_stats(dev->phydev, &stats, data);
2069*4882a593Smuzhiyun if (ret < 0)
2070*4882a593Smuzhiyun goto out;
2071*4882a593Smuzhiyun } else {
2072*4882a593Smuzhiyun ops->get_ethtool_phy_stats(dev, &stats, data);
2073*4882a593Smuzhiyun }
2074*4882a593Smuzhiyun } else {
2075*4882a593Smuzhiyun data = NULL;
2076*4882a593Smuzhiyun }
2077*4882a593Smuzhiyun
2078*4882a593Smuzhiyun ret = -EFAULT;
2079*4882a593Smuzhiyun if (copy_to_user(useraddr, &stats, sizeof(stats)))
2080*4882a593Smuzhiyun goto out;
2081*4882a593Smuzhiyun useraddr += sizeof(stats);
2082*4882a593Smuzhiyun if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
2083*4882a593Smuzhiyun goto out;
2084*4882a593Smuzhiyun ret = 0;
2085*4882a593Smuzhiyun
2086*4882a593Smuzhiyun out:
2087*4882a593Smuzhiyun vfree(data);
2088*4882a593Smuzhiyun return ret;
2089*4882a593Smuzhiyun }
2090*4882a593Smuzhiyun
ethtool_get_perm_addr(struct net_device * dev,void __user * useraddr)2091*4882a593Smuzhiyun static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
2092*4882a593Smuzhiyun {
2093*4882a593Smuzhiyun struct ethtool_perm_addr epaddr;
2094*4882a593Smuzhiyun
2095*4882a593Smuzhiyun if (copy_from_user(&epaddr, useraddr, sizeof(epaddr)))
2096*4882a593Smuzhiyun return -EFAULT;
2097*4882a593Smuzhiyun
2098*4882a593Smuzhiyun if (epaddr.size < dev->addr_len)
2099*4882a593Smuzhiyun return -ETOOSMALL;
2100*4882a593Smuzhiyun epaddr.size = dev->addr_len;
2101*4882a593Smuzhiyun
2102*4882a593Smuzhiyun if (copy_to_user(useraddr, &epaddr, sizeof(epaddr)))
2103*4882a593Smuzhiyun return -EFAULT;
2104*4882a593Smuzhiyun useraddr += sizeof(epaddr);
2105*4882a593Smuzhiyun if (copy_to_user(useraddr, dev->perm_addr, epaddr.size))
2106*4882a593Smuzhiyun return -EFAULT;
2107*4882a593Smuzhiyun return 0;
2108*4882a593Smuzhiyun }
2109*4882a593Smuzhiyun
ethtool_get_value(struct net_device * dev,char __user * useraddr,u32 cmd,u32 (* actor)(struct net_device *))2110*4882a593Smuzhiyun static int ethtool_get_value(struct net_device *dev, char __user *useraddr,
2111*4882a593Smuzhiyun u32 cmd, u32 (*actor)(struct net_device *))
2112*4882a593Smuzhiyun {
2113*4882a593Smuzhiyun struct ethtool_value edata = { .cmd = cmd };
2114*4882a593Smuzhiyun
2115*4882a593Smuzhiyun if (!actor)
2116*4882a593Smuzhiyun return -EOPNOTSUPP;
2117*4882a593Smuzhiyun
2118*4882a593Smuzhiyun edata.data = actor(dev);
2119*4882a593Smuzhiyun
2120*4882a593Smuzhiyun if (copy_to_user(useraddr, &edata, sizeof(edata)))
2121*4882a593Smuzhiyun return -EFAULT;
2122*4882a593Smuzhiyun return 0;
2123*4882a593Smuzhiyun }
2124*4882a593Smuzhiyun
ethtool_set_value_void(struct net_device * dev,char __user * useraddr,void (* actor)(struct net_device *,u32))2125*4882a593Smuzhiyun static int ethtool_set_value_void(struct net_device *dev, char __user *useraddr,
2126*4882a593Smuzhiyun void (*actor)(struct net_device *, u32))
2127*4882a593Smuzhiyun {
2128*4882a593Smuzhiyun struct ethtool_value edata;
2129*4882a593Smuzhiyun
2130*4882a593Smuzhiyun if (!actor)
2131*4882a593Smuzhiyun return -EOPNOTSUPP;
2132*4882a593Smuzhiyun
2133*4882a593Smuzhiyun if (copy_from_user(&edata, useraddr, sizeof(edata)))
2134*4882a593Smuzhiyun return -EFAULT;
2135*4882a593Smuzhiyun
2136*4882a593Smuzhiyun actor(dev, edata.data);
2137*4882a593Smuzhiyun return 0;
2138*4882a593Smuzhiyun }
2139*4882a593Smuzhiyun
ethtool_set_value(struct net_device * dev,char __user * useraddr,int (* actor)(struct net_device *,u32))2140*4882a593Smuzhiyun static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
2141*4882a593Smuzhiyun int (*actor)(struct net_device *, u32))
2142*4882a593Smuzhiyun {
2143*4882a593Smuzhiyun struct ethtool_value edata;
2144*4882a593Smuzhiyun
2145*4882a593Smuzhiyun if (!actor)
2146*4882a593Smuzhiyun return -EOPNOTSUPP;
2147*4882a593Smuzhiyun
2148*4882a593Smuzhiyun if (copy_from_user(&edata, useraddr, sizeof(edata)))
2149*4882a593Smuzhiyun return -EFAULT;
2150*4882a593Smuzhiyun
2151*4882a593Smuzhiyun return actor(dev, edata.data);
2152*4882a593Smuzhiyun }
2153*4882a593Smuzhiyun
ethtool_flash_device(struct net_device * dev,char __user * useraddr)2154*4882a593Smuzhiyun static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
2155*4882a593Smuzhiyun char __user *useraddr)
2156*4882a593Smuzhiyun {
2157*4882a593Smuzhiyun struct ethtool_flash efl;
2158*4882a593Smuzhiyun
2159*4882a593Smuzhiyun if (copy_from_user(&efl, useraddr, sizeof(efl)))
2160*4882a593Smuzhiyun return -EFAULT;
2161*4882a593Smuzhiyun efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
2162*4882a593Smuzhiyun
2163*4882a593Smuzhiyun if (!dev->ethtool_ops->flash_device)
2164*4882a593Smuzhiyun return devlink_compat_flash_update(dev, efl.data);
2165*4882a593Smuzhiyun
2166*4882a593Smuzhiyun return dev->ethtool_ops->flash_device(dev, &efl);
2167*4882a593Smuzhiyun }
2168*4882a593Smuzhiyun
ethtool_set_dump(struct net_device * dev,void __user * useraddr)2169*4882a593Smuzhiyun static int ethtool_set_dump(struct net_device *dev,
2170*4882a593Smuzhiyun void __user *useraddr)
2171*4882a593Smuzhiyun {
2172*4882a593Smuzhiyun struct ethtool_dump dump;
2173*4882a593Smuzhiyun
2174*4882a593Smuzhiyun if (!dev->ethtool_ops->set_dump)
2175*4882a593Smuzhiyun return -EOPNOTSUPP;
2176*4882a593Smuzhiyun
2177*4882a593Smuzhiyun if (copy_from_user(&dump, useraddr, sizeof(dump)))
2178*4882a593Smuzhiyun return -EFAULT;
2179*4882a593Smuzhiyun
2180*4882a593Smuzhiyun return dev->ethtool_ops->set_dump(dev, &dump);
2181*4882a593Smuzhiyun }
2182*4882a593Smuzhiyun
ethtool_get_dump_flag(struct net_device * dev,void __user * useraddr)2183*4882a593Smuzhiyun static int ethtool_get_dump_flag(struct net_device *dev,
2184*4882a593Smuzhiyun void __user *useraddr)
2185*4882a593Smuzhiyun {
2186*4882a593Smuzhiyun int ret;
2187*4882a593Smuzhiyun struct ethtool_dump dump;
2188*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
2189*4882a593Smuzhiyun
2190*4882a593Smuzhiyun if (!ops->get_dump_flag)
2191*4882a593Smuzhiyun return -EOPNOTSUPP;
2192*4882a593Smuzhiyun
2193*4882a593Smuzhiyun if (copy_from_user(&dump, useraddr, sizeof(dump)))
2194*4882a593Smuzhiyun return -EFAULT;
2195*4882a593Smuzhiyun
2196*4882a593Smuzhiyun ret = ops->get_dump_flag(dev, &dump);
2197*4882a593Smuzhiyun if (ret)
2198*4882a593Smuzhiyun return ret;
2199*4882a593Smuzhiyun
2200*4882a593Smuzhiyun if (copy_to_user(useraddr, &dump, sizeof(dump)))
2201*4882a593Smuzhiyun return -EFAULT;
2202*4882a593Smuzhiyun return 0;
2203*4882a593Smuzhiyun }
2204*4882a593Smuzhiyun
ethtool_get_dump_data(struct net_device * dev,void __user * useraddr)2205*4882a593Smuzhiyun static int ethtool_get_dump_data(struct net_device *dev,
2206*4882a593Smuzhiyun void __user *useraddr)
2207*4882a593Smuzhiyun {
2208*4882a593Smuzhiyun int ret;
2209*4882a593Smuzhiyun __u32 len;
2210*4882a593Smuzhiyun struct ethtool_dump dump, tmp;
2211*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
2212*4882a593Smuzhiyun void *data = NULL;
2213*4882a593Smuzhiyun
2214*4882a593Smuzhiyun if (!ops->get_dump_data || !ops->get_dump_flag)
2215*4882a593Smuzhiyun return -EOPNOTSUPP;
2216*4882a593Smuzhiyun
2217*4882a593Smuzhiyun if (copy_from_user(&dump, useraddr, sizeof(dump)))
2218*4882a593Smuzhiyun return -EFAULT;
2219*4882a593Smuzhiyun
2220*4882a593Smuzhiyun memset(&tmp, 0, sizeof(tmp));
2221*4882a593Smuzhiyun tmp.cmd = ETHTOOL_GET_DUMP_FLAG;
2222*4882a593Smuzhiyun ret = ops->get_dump_flag(dev, &tmp);
2223*4882a593Smuzhiyun if (ret)
2224*4882a593Smuzhiyun return ret;
2225*4882a593Smuzhiyun
2226*4882a593Smuzhiyun len = min(tmp.len, dump.len);
2227*4882a593Smuzhiyun if (!len)
2228*4882a593Smuzhiyun return -EFAULT;
2229*4882a593Smuzhiyun
2230*4882a593Smuzhiyun /* Don't ever let the driver think there's more space available
2231*4882a593Smuzhiyun * than it requested with .get_dump_flag().
2232*4882a593Smuzhiyun */
2233*4882a593Smuzhiyun dump.len = len;
2234*4882a593Smuzhiyun
2235*4882a593Smuzhiyun /* Always allocate enough space to hold the whole thing so that the
2236*4882a593Smuzhiyun * driver does not need to check the length and bother with partial
2237*4882a593Smuzhiyun * dumping.
2238*4882a593Smuzhiyun */
2239*4882a593Smuzhiyun data = vzalloc(tmp.len);
2240*4882a593Smuzhiyun if (!data)
2241*4882a593Smuzhiyun return -ENOMEM;
2242*4882a593Smuzhiyun ret = ops->get_dump_data(dev, &dump, data);
2243*4882a593Smuzhiyun if (ret)
2244*4882a593Smuzhiyun goto out;
2245*4882a593Smuzhiyun
2246*4882a593Smuzhiyun /* There are two sane possibilities:
2247*4882a593Smuzhiyun * 1. The driver's .get_dump_data() does not touch dump.len.
2248*4882a593Smuzhiyun * 2. Or it may set dump.len to how much it really writes, which
2249*4882a593Smuzhiyun * should be tmp.len (or len if it can do a partial dump).
2250*4882a593Smuzhiyun * In any case respond to userspace with the actual length of data
2251*4882a593Smuzhiyun * it's receiving.
2252*4882a593Smuzhiyun */
2253*4882a593Smuzhiyun WARN_ON(dump.len != len && dump.len != tmp.len);
2254*4882a593Smuzhiyun dump.len = len;
2255*4882a593Smuzhiyun
2256*4882a593Smuzhiyun if (copy_to_user(useraddr, &dump, sizeof(dump))) {
2257*4882a593Smuzhiyun ret = -EFAULT;
2258*4882a593Smuzhiyun goto out;
2259*4882a593Smuzhiyun }
2260*4882a593Smuzhiyun useraddr += offsetof(struct ethtool_dump, data);
2261*4882a593Smuzhiyun if (copy_to_user(useraddr, data, len))
2262*4882a593Smuzhiyun ret = -EFAULT;
2263*4882a593Smuzhiyun out:
2264*4882a593Smuzhiyun vfree(data);
2265*4882a593Smuzhiyun return ret;
2266*4882a593Smuzhiyun }
2267*4882a593Smuzhiyun
ethtool_get_ts_info(struct net_device * dev,void __user * useraddr)2268*4882a593Smuzhiyun static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr)
2269*4882a593Smuzhiyun {
2270*4882a593Smuzhiyun struct ethtool_ts_info info;
2271*4882a593Smuzhiyun int err;
2272*4882a593Smuzhiyun
2273*4882a593Smuzhiyun err = __ethtool_get_ts_info(dev, &info);
2274*4882a593Smuzhiyun if (err)
2275*4882a593Smuzhiyun return err;
2276*4882a593Smuzhiyun
2277*4882a593Smuzhiyun if (copy_to_user(useraddr, &info, sizeof(info)))
2278*4882a593Smuzhiyun return -EFAULT;
2279*4882a593Smuzhiyun
2280*4882a593Smuzhiyun return 0;
2281*4882a593Smuzhiyun }
2282*4882a593Smuzhiyun
__ethtool_get_module_info(struct net_device * dev,struct ethtool_modinfo * modinfo)2283*4882a593Smuzhiyun static int __ethtool_get_module_info(struct net_device *dev,
2284*4882a593Smuzhiyun struct ethtool_modinfo *modinfo)
2285*4882a593Smuzhiyun {
2286*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
2287*4882a593Smuzhiyun struct phy_device *phydev = dev->phydev;
2288*4882a593Smuzhiyun
2289*4882a593Smuzhiyun if (dev->sfp_bus)
2290*4882a593Smuzhiyun return sfp_get_module_info(dev->sfp_bus, modinfo);
2291*4882a593Smuzhiyun
2292*4882a593Smuzhiyun if (phydev && phydev->drv && phydev->drv->module_info)
2293*4882a593Smuzhiyun return phydev->drv->module_info(phydev, modinfo);
2294*4882a593Smuzhiyun
2295*4882a593Smuzhiyun if (ops->get_module_info)
2296*4882a593Smuzhiyun return ops->get_module_info(dev, modinfo);
2297*4882a593Smuzhiyun
2298*4882a593Smuzhiyun return -EOPNOTSUPP;
2299*4882a593Smuzhiyun }
2300*4882a593Smuzhiyun
ethtool_get_module_info(struct net_device * dev,void __user * useraddr)2301*4882a593Smuzhiyun static int ethtool_get_module_info(struct net_device *dev,
2302*4882a593Smuzhiyun void __user *useraddr)
2303*4882a593Smuzhiyun {
2304*4882a593Smuzhiyun int ret;
2305*4882a593Smuzhiyun struct ethtool_modinfo modinfo;
2306*4882a593Smuzhiyun
2307*4882a593Smuzhiyun if (copy_from_user(&modinfo, useraddr, sizeof(modinfo)))
2308*4882a593Smuzhiyun return -EFAULT;
2309*4882a593Smuzhiyun
2310*4882a593Smuzhiyun ret = __ethtool_get_module_info(dev, &modinfo);
2311*4882a593Smuzhiyun if (ret)
2312*4882a593Smuzhiyun return ret;
2313*4882a593Smuzhiyun
2314*4882a593Smuzhiyun if (copy_to_user(useraddr, &modinfo, sizeof(modinfo)))
2315*4882a593Smuzhiyun return -EFAULT;
2316*4882a593Smuzhiyun
2317*4882a593Smuzhiyun return 0;
2318*4882a593Smuzhiyun }
2319*4882a593Smuzhiyun
__ethtool_get_module_eeprom(struct net_device * dev,struct ethtool_eeprom * ee,u8 * data)2320*4882a593Smuzhiyun static int __ethtool_get_module_eeprom(struct net_device *dev,
2321*4882a593Smuzhiyun struct ethtool_eeprom *ee, u8 *data)
2322*4882a593Smuzhiyun {
2323*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
2324*4882a593Smuzhiyun struct phy_device *phydev = dev->phydev;
2325*4882a593Smuzhiyun
2326*4882a593Smuzhiyun if (dev->sfp_bus)
2327*4882a593Smuzhiyun return sfp_get_module_eeprom(dev->sfp_bus, ee, data);
2328*4882a593Smuzhiyun
2329*4882a593Smuzhiyun if (phydev && phydev->drv && phydev->drv->module_eeprom)
2330*4882a593Smuzhiyun return phydev->drv->module_eeprom(phydev, ee, data);
2331*4882a593Smuzhiyun
2332*4882a593Smuzhiyun if (ops->get_module_eeprom)
2333*4882a593Smuzhiyun return ops->get_module_eeprom(dev, ee, data);
2334*4882a593Smuzhiyun
2335*4882a593Smuzhiyun return -EOPNOTSUPP;
2336*4882a593Smuzhiyun }
2337*4882a593Smuzhiyun
ethtool_get_module_eeprom(struct net_device * dev,void __user * useraddr)2338*4882a593Smuzhiyun static int ethtool_get_module_eeprom(struct net_device *dev,
2339*4882a593Smuzhiyun void __user *useraddr)
2340*4882a593Smuzhiyun {
2341*4882a593Smuzhiyun int ret;
2342*4882a593Smuzhiyun struct ethtool_modinfo modinfo;
2343*4882a593Smuzhiyun
2344*4882a593Smuzhiyun ret = __ethtool_get_module_info(dev, &modinfo);
2345*4882a593Smuzhiyun if (ret)
2346*4882a593Smuzhiyun return ret;
2347*4882a593Smuzhiyun
2348*4882a593Smuzhiyun return ethtool_get_any_eeprom(dev, useraddr,
2349*4882a593Smuzhiyun __ethtool_get_module_eeprom,
2350*4882a593Smuzhiyun modinfo.eeprom_len);
2351*4882a593Smuzhiyun }
2352*4882a593Smuzhiyun
ethtool_tunable_valid(const struct ethtool_tunable * tuna)2353*4882a593Smuzhiyun static int ethtool_tunable_valid(const struct ethtool_tunable *tuna)
2354*4882a593Smuzhiyun {
2355*4882a593Smuzhiyun switch (tuna->id) {
2356*4882a593Smuzhiyun case ETHTOOL_RX_COPYBREAK:
2357*4882a593Smuzhiyun case ETHTOOL_TX_COPYBREAK:
2358*4882a593Smuzhiyun if (tuna->len != sizeof(u32) ||
2359*4882a593Smuzhiyun tuna->type_id != ETHTOOL_TUNABLE_U32)
2360*4882a593Smuzhiyun return -EINVAL;
2361*4882a593Smuzhiyun break;
2362*4882a593Smuzhiyun case ETHTOOL_PFC_PREVENTION_TOUT:
2363*4882a593Smuzhiyun if (tuna->len != sizeof(u16) ||
2364*4882a593Smuzhiyun tuna->type_id != ETHTOOL_TUNABLE_U16)
2365*4882a593Smuzhiyun return -EINVAL;
2366*4882a593Smuzhiyun break;
2367*4882a593Smuzhiyun default:
2368*4882a593Smuzhiyun return -EINVAL;
2369*4882a593Smuzhiyun }
2370*4882a593Smuzhiyun
2371*4882a593Smuzhiyun return 0;
2372*4882a593Smuzhiyun }
2373*4882a593Smuzhiyun
ethtool_get_tunable(struct net_device * dev,void __user * useraddr)2374*4882a593Smuzhiyun static int ethtool_get_tunable(struct net_device *dev, void __user *useraddr)
2375*4882a593Smuzhiyun {
2376*4882a593Smuzhiyun int ret;
2377*4882a593Smuzhiyun struct ethtool_tunable tuna;
2378*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
2379*4882a593Smuzhiyun void *data;
2380*4882a593Smuzhiyun
2381*4882a593Smuzhiyun if (!ops->get_tunable)
2382*4882a593Smuzhiyun return -EOPNOTSUPP;
2383*4882a593Smuzhiyun if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
2384*4882a593Smuzhiyun return -EFAULT;
2385*4882a593Smuzhiyun ret = ethtool_tunable_valid(&tuna);
2386*4882a593Smuzhiyun if (ret)
2387*4882a593Smuzhiyun return ret;
2388*4882a593Smuzhiyun data = kzalloc(tuna.len, GFP_USER);
2389*4882a593Smuzhiyun if (!data)
2390*4882a593Smuzhiyun return -ENOMEM;
2391*4882a593Smuzhiyun ret = ops->get_tunable(dev, &tuna, data);
2392*4882a593Smuzhiyun if (ret)
2393*4882a593Smuzhiyun goto out;
2394*4882a593Smuzhiyun useraddr += sizeof(tuna);
2395*4882a593Smuzhiyun ret = -EFAULT;
2396*4882a593Smuzhiyun if (copy_to_user(useraddr, data, tuna.len))
2397*4882a593Smuzhiyun goto out;
2398*4882a593Smuzhiyun ret = 0;
2399*4882a593Smuzhiyun
2400*4882a593Smuzhiyun out:
2401*4882a593Smuzhiyun kfree(data);
2402*4882a593Smuzhiyun return ret;
2403*4882a593Smuzhiyun }
2404*4882a593Smuzhiyun
ethtool_set_tunable(struct net_device * dev,void __user * useraddr)2405*4882a593Smuzhiyun static int ethtool_set_tunable(struct net_device *dev, void __user *useraddr)
2406*4882a593Smuzhiyun {
2407*4882a593Smuzhiyun int ret;
2408*4882a593Smuzhiyun struct ethtool_tunable tuna;
2409*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
2410*4882a593Smuzhiyun void *data;
2411*4882a593Smuzhiyun
2412*4882a593Smuzhiyun if (!ops->set_tunable)
2413*4882a593Smuzhiyun return -EOPNOTSUPP;
2414*4882a593Smuzhiyun if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
2415*4882a593Smuzhiyun return -EFAULT;
2416*4882a593Smuzhiyun ret = ethtool_tunable_valid(&tuna);
2417*4882a593Smuzhiyun if (ret)
2418*4882a593Smuzhiyun return ret;
2419*4882a593Smuzhiyun useraddr += sizeof(tuna);
2420*4882a593Smuzhiyun data = memdup_user(useraddr, tuna.len);
2421*4882a593Smuzhiyun if (IS_ERR(data))
2422*4882a593Smuzhiyun return PTR_ERR(data);
2423*4882a593Smuzhiyun ret = ops->set_tunable(dev, &tuna, data);
2424*4882a593Smuzhiyun
2425*4882a593Smuzhiyun kfree(data);
2426*4882a593Smuzhiyun return ret;
2427*4882a593Smuzhiyun }
2428*4882a593Smuzhiyun
2429*4882a593Smuzhiyun static noinline_for_stack int
ethtool_get_per_queue_coalesce(struct net_device * dev,void __user * useraddr,struct ethtool_per_queue_op * per_queue_opt)2430*4882a593Smuzhiyun ethtool_get_per_queue_coalesce(struct net_device *dev,
2431*4882a593Smuzhiyun void __user *useraddr,
2432*4882a593Smuzhiyun struct ethtool_per_queue_op *per_queue_opt)
2433*4882a593Smuzhiyun {
2434*4882a593Smuzhiyun u32 bit;
2435*4882a593Smuzhiyun int ret;
2436*4882a593Smuzhiyun DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
2437*4882a593Smuzhiyun
2438*4882a593Smuzhiyun if (!dev->ethtool_ops->get_per_queue_coalesce)
2439*4882a593Smuzhiyun return -EOPNOTSUPP;
2440*4882a593Smuzhiyun
2441*4882a593Smuzhiyun useraddr += sizeof(*per_queue_opt);
2442*4882a593Smuzhiyun
2443*4882a593Smuzhiyun bitmap_from_arr32(queue_mask, per_queue_opt->queue_mask,
2444*4882a593Smuzhiyun MAX_NUM_QUEUE);
2445*4882a593Smuzhiyun
2446*4882a593Smuzhiyun for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
2447*4882a593Smuzhiyun struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
2448*4882a593Smuzhiyun
2449*4882a593Smuzhiyun ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, &coalesce);
2450*4882a593Smuzhiyun if (ret != 0)
2451*4882a593Smuzhiyun return ret;
2452*4882a593Smuzhiyun if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
2453*4882a593Smuzhiyun return -EFAULT;
2454*4882a593Smuzhiyun useraddr += sizeof(coalesce);
2455*4882a593Smuzhiyun }
2456*4882a593Smuzhiyun
2457*4882a593Smuzhiyun return 0;
2458*4882a593Smuzhiyun }
2459*4882a593Smuzhiyun
2460*4882a593Smuzhiyun static noinline_for_stack int
ethtool_set_per_queue_coalesce(struct net_device * dev,void __user * useraddr,struct ethtool_per_queue_op * per_queue_opt)2461*4882a593Smuzhiyun ethtool_set_per_queue_coalesce(struct net_device *dev,
2462*4882a593Smuzhiyun void __user *useraddr,
2463*4882a593Smuzhiyun struct ethtool_per_queue_op *per_queue_opt)
2464*4882a593Smuzhiyun {
2465*4882a593Smuzhiyun u32 bit;
2466*4882a593Smuzhiyun int i, ret = 0;
2467*4882a593Smuzhiyun int n_queue;
2468*4882a593Smuzhiyun struct ethtool_coalesce *backup = NULL, *tmp = NULL;
2469*4882a593Smuzhiyun DECLARE_BITMAP(queue_mask, MAX_NUM_QUEUE);
2470*4882a593Smuzhiyun
2471*4882a593Smuzhiyun if ((!dev->ethtool_ops->set_per_queue_coalesce) ||
2472*4882a593Smuzhiyun (!dev->ethtool_ops->get_per_queue_coalesce))
2473*4882a593Smuzhiyun return -EOPNOTSUPP;
2474*4882a593Smuzhiyun
2475*4882a593Smuzhiyun useraddr += sizeof(*per_queue_opt);
2476*4882a593Smuzhiyun
2477*4882a593Smuzhiyun bitmap_from_arr32(queue_mask, per_queue_opt->queue_mask, MAX_NUM_QUEUE);
2478*4882a593Smuzhiyun n_queue = bitmap_weight(queue_mask, MAX_NUM_QUEUE);
2479*4882a593Smuzhiyun tmp = backup = kmalloc_array(n_queue, sizeof(*backup), GFP_KERNEL);
2480*4882a593Smuzhiyun if (!backup)
2481*4882a593Smuzhiyun return -ENOMEM;
2482*4882a593Smuzhiyun
2483*4882a593Smuzhiyun for_each_set_bit(bit, queue_mask, MAX_NUM_QUEUE) {
2484*4882a593Smuzhiyun struct ethtool_coalesce coalesce;
2485*4882a593Smuzhiyun
2486*4882a593Smuzhiyun ret = dev->ethtool_ops->get_per_queue_coalesce(dev, bit, tmp);
2487*4882a593Smuzhiyun if (ret != 0)
2488*4882a593Smuzhiyun goto roll_back;
2489*4882a593Smuzhiyun
2490*4882a593Smuzhiyun tmp++;
2491*4882a593Smuzhiyun
2492*4882a593Smuzhiyun if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) {
2493*4882a593Smuzhiyun ret = -EFAULT;
2494*4882a593Smuzhiyun goto roll_back;
2495*4882a593Smuzhiyun }
2496*4882a593Smuzhiyun
2497*4882a593Smuzhiyun if (!ethtool_set_coalesce_supported(dev, &coalesce)) {
2498*4882a593Smuzhiyun ret = -EOPNOTSUPP;
2499*4882a593Smuzhiyun goto roll_back;
2500*4882a593Smuzhiyun }
2501*4882a593Smuzhiyun
2502*4882a593Smuzhiyun ret = dev->ethtool_ops->set_per_queue_coalesce(dev, bit, &coalesce);
2503*4882a593Smuzhiyun if (ret != 0)
2504*4882a593Smuzhiyun goto roll_back;
2505*4882a593Smuzhiyun
2506*4882a593Smuzhiyun useraddr += sizeof(coalesce);
2507*4882a593Smuzhiyun }
2508*4882a593Smuzhiyun
2509*4882a593Smuzhiyun roll_back:
2510*4882a593Smuzhiyun if (ret != 0) {
2511*4882a593Smuzhiyun tmp = backup;
2512*4882a593Smuzhiyun for_each_set_bit(i, queue_mask, bit) {
2513*4882a593Smuzhiyun dev->ethtool_ops->set_per_queue_coalesce(dev, i, tmp);
2514*4882a593Smuzhiyun tmp++;
2515*4882a593Smuzhiyun }
2516*4882a593Smuzhiyun }
2517*4882a593Smuzhiyun kfree(backup);
2518*4882a593Smuzhiyun
2519*4882a593Smuzhiyun return ret;
2520*4882a593Smuzhiyun }
2521*4882a593Smuzhiyun
ethtool_set_per_queue(struct net_device * dev,void __user * useraddr,u32 sub_cmd)2522*4882a593Smuzhiyun static int noinline_for_stack ethtool_set_per_queue(struct net_device *dev,
2523*4882a593Smuzhiyun void __user *useraddr, u32 sub_cmd)
2524*4882a593Smuzhiyun {
2525*4882a593Smuzhiyun struct ethtool_per_queue_op per_queue_opt;
2526*4882a593Smuzhiyun
2527*4882a593Smuzhiyun if (copy_from_user(&per_queue_opt, useraddr, sizeof(per_queue_opt)))
2528*4882a593Smuzhiyun return -EFAULT;
2529*4882a593Smuzhiyun
2530*4882a593Smuzhiyun if (per_queue_opt.sub_command != sub_cmd)
2531*4882a593Smuzhiyun return -EINVAL;
2532*4882a593Smuzhiyun
2533*4882a593Smuzhiyun switch (per_queue_opt.sub_command) {
2534*4882a593Smuzhiyun case ETHTOOL_GCOALESCE:
2535*4882a593Smuzhiyun return ethtool_get_per_queue_coalesce(dev, useraddr, &per_queue_opt);
2536*4882a593Smuzhiyun case ETHTOOL_SCOALESCE:
2537*4882a593Smuzhiyun return ethtool_set_per_queue_coalesce(dev, useraddr, &per_queue_opt);
2538*4882a593Smuzhiyun default:
2539*4882a593Smuzhiyun return -EOPNOTSUPP;
2540*4882a593Smuzhiyun };
2541*4882a593Smuzhiyun }
2542*4882a593Smuzhiyun
ethtool_phy_tunable_valid(const struct ethtool_tunable * tuna)2543*4882a593Smuzhiyun static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna)
2544*4882a593Smuzhiyun {
2545*4882a593Smuzhiyun switch (tuna->id) {
2546*4882a593Smuzhiyun case ETHTOOL_PHY_DOWNSHIFT:
2547*4882a593Smuzhiyun case ETHTOOL_PHY_FAST_LINK_DOWN:
2548*4882a593Smuzhiyun if (tuna->len != sizeof(u8) ||
2549*4882a593Smuzhiyun tuna->type_id != ETHTOOL_TUNABLE_U8)
2550*4882a593Smuzhiyun return -EINVAL;
2551*4882a593Smuzhiyun break;
2552*4882a593Smuzhiyun case ETHTOOL_PHY_EDPD:
2553*4882a593Smuzhiyun if (tuna->len != sizeof(u16) ||
2554*4882a593Smuzhiyun tuna->type_id != ETHTOOL_TUNABLE_U16)
2555*4882a593Smuzhiyun return -EINVAL;
2556*4882a593Smuzhiyun break;
2557*4882a593Smuzhiyun default:
2558*4882a593Smuzhiyun return -EINVAL;
2559*4882a593Smuzhiyun }
2560*4882a593Smuzhiyun
2561*4882a593Smuzhiyun return 0;
2562*4882a593Smuzhiyun }
2563*4882a593Smuzhiyun
get_phy_tunable(struct net_device * dev,void __user * useraddr)2564*4882a593Smuzhiyun static int get_phy_tunable(struct net_device *dev, void __user *useraddr)
2565*4882a593Smuzhiyun {
2566*4882a593Smuzhiyun struct phy_device *phydev = dev->phydev;
2567*4882a593Smuzhiyun struct ethtool_tunable tuna;
2568*4882a593Smuzhiyun bool phy_drv_tunable;
2569*4882a593Smuzhiyun void *data;
2570*4882a593Smuzhiyun int ret;
2571*4882a593Smuzhiyun
2572*4882a593Smuzhiyun phy_drv_tunable = phydev && phydev->drv && phydev->drv->get_tunable;
2573*4882a593Smuzhiyun if (!phy_drv_tunable && !dev->ethtool_ops->get_phy_tunable)
2574*4882a593Smuzhiyun return -EOPNOTSUPP;
2575*4882a593Smuzhiyun if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
2576*4882a593Smuzhiyun return -EFAULT;
2577*4882a593Smuzhiyun ret = ethtool_phy_tunable_valid(&tuna);
2578*4882a593Smuzhiyun if (ret)
2579*4882a593Smuzhiyun return ret;
2580*4882a593Smuzhiyun data = kzalloc(tuna.len, GFP_USER);
2581*4882a593Smuzhiyun if (!data)
2582*4882a593Smuzhiyun return -ENOMEM;
2583*4882a593Smuzhiyun if (phy_drv_tunable) {
2584*4882a593Smuzhiyun mutex_lock(&phydev->lock);
2585*4882a593Smuzhiyun ret = phydev->drv->get_tunable(phydev, &tuna, data);
2586*4882a593Smuzhiyun mutex_unlock(&phydev->lock);
2587*4882a593Smuzhiyun } else {
2588*4882a593Smuzhiyun ret = dev->ethtool_ops->get_phy_tunable(dev, &tuna, data);
2589*4882a593Smuzhiyun }
2590*4882a593Smuzhiyun if (ret)
2591*4882a593Smuzhiyun goto out;
2592*4882a593Smuzhiyun useraddr += sizeof(tuna);
2593*4882a593Smuzhiyun ret = -EFAULT;
2594*4882a593Smuzhiyun if (copy_to_user(useraddr, data, tuna.len))
2595*4882a593Smuzhiyun goto out;
2596*4882a593Smuzhiyun ret = 0;
2597*4882a593Smuzhiyun
2598*4882a593Smuzhiyun out:
2599*4882a593Smuzhiyun kfree(data);
2600*4882a593Smuzhiyun return ret;
2601*4882a593Smuzhiyun }
2602*4882a593Smuzhiyun
set_phy_tunable(struct net_device * dev,void __user * useraddr)2603*4882a593Smuzhiyun static int set_phy_tunable(struct net_device *dev, void __user *useraddr)
2604*4882a593Smuzhiyun {
2605*4882a593Smuzhiyun struct phy_device *phydev = dev->phydev;
2606*4882a593Smuzhiyun struct ethtool_tunable tuna;
2607*4882a593Smuzhiyun bool phy_drv_tunable;
2608*4882a593Smuzhiyun void *data;
2609*4882a593Smuzhiyun int ret;
2610*4882a593Smuzhiyun
2611*4882a593Smuzhiyun phy_drv_tunable = phydev && phydev->drv && phydev->drv->get_tunable;
2612*4882a593Smuzhiyun if (!phy_drv_tunable && !dev->ethtool_ops->set_phy_tunable)
2613*4882a593Smuzhiyun return -EOPNOTSUPP;
2614*4882a593Smuzhiyun if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
2615*4882a593Smuzhiyun return -EFAULT;
2616*4882a593Smuzhiyun ret = ethtool_phy_tunable_valid(&tuna);
2617*4882a593Smuzhiyun if (ret)
2618*4882a593Smuzhiyun return ret;
2619*4882a593Smuzhiyun useraddr += sizeof(tuna);
2620*4882a593Smuzhiyun data = memdup_user(useraddr, tuna.len);
2621*4882a593Smuzhiyun if (IS_ERR(data))
2622*4882a593Smuzhiyun return PTR_ERR(data);
2623*4882a593Smuzhiyun if (phy_drv_tunable) {
2624*4882a593Smuzhiyun mutex_lock(&phydev->lock);
2625*4882a593Smuzhiyun ret = phydev->drv->set_tunable(phydev, &tuna, data);
2626*4882a593Smuzhiyun mutex_unlock(&phydev->lock);
2627*4882a593Smuzhiyun } else {
2628*4882a593Smuzhiyun ret = dev->ethtool_ops->set_phy_tunable(dev, &tuna, data);
2629*4882a593Smuzhiyun }
2630*4882a593Smuzhiyun
2631*4882a593Smuzhiyun kfree(data);
2632*4882a593Smuzhiyun return ret;
2633*4882a593Smuzhiyun }
2634*4882a593Smuzhiyun
ethtool_get_fecparam(struct net_device * dev,void __user * useraddr)2635*4882a593Smuzhiyun static int ethtool_get_fecparam(struct net_device *dev, void __user *useraddr)
2636*4882a593Smuzhiyun {
2637*4882a593Smuzhiyun struct ethtool_fecparam fecparam = { .cmd = ETHTOOL_GFECPARAM };
2638*4882a593Smuzhiyun int rc;
2639*4882a593Smuzhiyun
2640*4882a593Smuzhiyun if (!dev->ethtool_ops->get_fecparam)
2641*4882a593Smuzhiyun return -EOPNOTSUPP;
2642*4882a593Smuzhiyun
2643*4882a593Smuzhiyun rc = dev->ethtool_ops->get_fecparam(dev, &fecparam);
2644*4882a593Smuzhiyun if (rc)
2645*4882a593Smuzhiyun return rc;
2646*4882a593Smuzhiyun
2647*4882a593Smuzhiyun if (copy_to_user(useraddr, &fecparam, sizeof(fecparam)))
2648*4882a593Smuzhiyun return -EFAULT;
2649*4882a593Smuzhiyun return 0;
2650*4882a593Smuzhiyun }
2651*4882a593Smuzhiyun
ethtool_set_fecparam(struct net_device * dev,void __user * useraddr)2652*4882a593Smuzhiyun static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr)
2653*4882a593Smuzhiyun {
2654*4882a593Smuzhiyun struct ethtool_fecparam fecparam;
2655*4882a593Smuzhiyun
2656*4882a593Smuzhiyun if (!dev->ethtool_ops->set_fecparam)
2657*4882a593Smuzhiyun return -EOPNOTSUPP;
2658*4882a593Smuzhiyun
2659*4882a593Smuzhiyun if (copy_from_user(&fecparam, useraddr, sizeof(fecparam)))
2660*4882a593Smuzhiyun return -EFAULT;
2661*4882a593Smuzhiyun
2662*4882a593Smuzhiyun return dev->ethtool_ops->set_fecparam(dev, &fecparam);
2663*4882a593Smuzhiyun }
2664*4882a593Smuzhiyun
2665*4882a593Smuzhiyun /* The main entry point in this file. Called from net/core/dev_ioctl.c */
2666*4882a593Smuzhiyun
dev_ethtool(struct net * net,struct ifreq * ifr)2667*4882a593Smuzhiyun int dev_ethtool(struct net *net, struct ifreq *ifr)
2668*4882a593Smuzhiyun {
2669*4882a593Smuzhiyun struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
2670*4882a593Smuzhiyun void __user *useraddr = ifr->ifr_data;
2671*4882a593Smuzhiyun u32 ethcmd, sub_cmd;
2672*4882a593Smuzhiyun int rc;
2673*4882a593Smuzhiyun netdev_features_t old_features;
2674*4882a593Smuzhiyun
2675*4882a593Smuzhiyun if (!dev || !netif_device_present(dev))
2676*4882a593Smuzhiyun return -ENODEV;
2677*4882a593Smuzhiyun
2678*4882a593Smuzhiyun if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
2679*4882a593Smuzhiyun return -EFAULT;
2680*4882a593Smuzhiyun
2681*4882a593Smuzhiyun if (ethcmd == ETHTOOL_PERQUEUE) {
2682*4882a593Smuzhiyun if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd)))
2683*4882a593Smuzhiyun return -EFAULT;
2684*4882a593Smuzhiyun } else {
2685*4882a593Smuzhiyun sub_cmd = ethcmd;
2686*4882a593Smuzhiyun }
2687*4882a593Smuzhiyun /* Allow some commands to be done by anyone */
2688*4882a593Smuzhiyun switch (sub_cmd) {
2689*4882a593Smuzhiyun case ETHTOOL_GSET:
2690*4882a593Smuzhiyun case ETHTOOL_GDRVINFO:
2691*4882a593Smuzhiyun case ETHTOOL_GMSGLVL:
2692*4882a593Smuzhiyun case ETHTOOL_GLINK:
2693*4882a593Smuzhiyun case ETHTOOL_GCOALESCE:
2694*4882a593Smuzhiyun case ETHTOOL_GRINGPARAM:
2695*4882a593Smuzhiyun case ETHTOOL_GPAUSEPARAM:
2696*4882a593Smuzhiyun case ETHTOOL_GRXCSUM:
2697*4882a593Smuzhiyun case ETHTOOL_GTXCSUM:
2698*4882a593Smuzhiyun case ETHTOOL_GSG:
2699*4882a593Smuzhiyun case ETHTOOL_GSSET_INFO:
2700*4882a593Smuzhiyun case ETHTOOL_GSTRINGS:
2701*4882a593Smuzhiyun case ETHTOOL_GSTATS:
2702*4882a593Smuzhiyun case ETHTOOL_GPHYSTATS:
2703*4882a593Smuzhiyun case ETHTOOL_GTSO:
2704*4882a593Smuzhiyun case ETHTOOL_GPERMADDR:
2705*4882a593Smuzhiyun case ETHTOOL_GUFO:
2706*4882a593Smuzhiyun case ETHTOOL_GGSO:
2707*4882a593Smuzhiyun case ETHTOOL_GGRO:
2708*4882a593Smuzhiyun case ETHTOOL_GFLAGS:
2709*4882a593Smuzhiyun case ETHTOOL_GPFLAGS:
2710*4882a593Smuzhiyun case ETHTOOL_GRXFH:
2711*4882a593Smuzhiyun case ETHTOOL_GRXRINGS:
2712*4882a593Smuzhiyun case ETHTOOL_GRXCLSRLCNT:
2713*4882a593Smuzhiyun case ETHTOOL_GRXCLSRULE:
2714*4882a593Smuzhiyun case ETHTOOL_GRXCLSRLALL:
2715*4882a593Smuzhiyun case ETHTOOL_GRXFHINDIR:
2716*4882a593Smuzhiyun case ETHTOOL_GRSSH:
2717*4882a593Smuzhiyun case ETHTOOL_GFEATURES:
2718*4882a593Smuzhiyun case ETHTOOL_GCHANNELS:
2719*4882a593Smuzhiyun case ETHTOOL_GET_TS_INFO:
2720*4882a593Smuzhiyun case ETHTOOL_GEEE:
2721*4882a593Smuzhiyun case ETHTOOL_GTUNABLE:
2722*4882a593Smuzhiyun case ETHTOOL_PHY_GTUNABLE:
2723*4882a593Smuzhiyun case ETHTOOL_GLINKSETTINGS:
2724*4882a593Smuzhiyun case ETHTOOL_GFECPARAM:
2725*4882a593Smuzhiyun break;
2726*4882a593Smuzhiyun default:
2727*4882a593Smuzhiyun if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
2728*4882a593Smuzhiyun return -EPERM;
2729*4882a593Smuzhiyun }
2730*4882a593Smuzhiyun
2731*4882a593Smuzhiyun if (dev->ethtool_ops->begin) {
2732*4882a593Smuzhiyun rc = dev->ethtool_ops->begin(dev);
2733*4882a593Smuzhiyun if (rc < 0)
2734*4882a593Smuzhiyun return rc;
2735*4882a593Smuzhiyun }
2736*4882a593Smuzhiyun old_features = dev->features;
2737*4882a593Smuzhiyun
2738*4882a593Smuzhiyun switch (ethcmd) {
2739*4882a593Smuzhiyun case ETHTOOL_GSET:
2740*4882a593Smuzhiyun rc = ethtool_get_settings(dev, useraddr);
2741*4882a593Smuzhiyun break;
2742*4882a593Smuzhiyun case ETHTOOL_SSET:
2743*4882a593Smuzhiyun rc = ethtool_set_settings(dev, useraddr);
2744*4882a593Smuzhiyun break;
2745*4882a593Smuzhiyun case ETHTOOL_GDRVINFO:
2746*4882a593Smuzhiyun rc = ethtool_get_drvinfo(dev, useraddr);
2747*4882a593Smuzhiyun break;
2748*4882a593Smuzhiyun case ETHTOOL_GREGS:
2749*4882a593Smuzhiyun rc = ethtool_get_regs(dev, useraddr);
2750*4882a593Smuzhiyun break;
2751*4882a593Smuzhiyun case ETHTOOL_GWOL:
2752*4882a593Smuzhiyun rc = ethtool_get_wol(dev, useraddr);
2753*4882a593Smuzhiyun break;
2754*4882a593Smuzhiyun case ETHTOOL_SWOL:
2755*4882a593Smuzhiyun rc = ethtool_set_wol(dev, useraddr);
2756*4882a593Smuzhiyun break;
2757*4882a593Smuzhiyun case ETHTOOL_GMSGLVL:
2758*4882a593Smuzhiyun rc = ethtool_get_value(dev, useraddr, ethcmd,
2759*4882a593Smuzhiyun dev->ethtool_ops->get_msglevel);
2760*4882a593Smuzhiyun break;
2761*4882a593Smuzhiyun case ETHTOOL_SMSGLVL:
2762*4882a593Smuzhiyun rc = ethtool_set_value_void(dev, useraddr,
2763*4882a593Smuzhiyun dev->ethtool_ops->set_msglevel);
2764*4882a593Smuzhiyun if (!rc)
2765*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_DEBUG_NTF, NULL);
2766*4882a593Smuzhiyun break;
2767*4882a593Smuzhiyun case ETHTOOL_GEEE:
2768*4882a593Smuzhiyun rc = ethtool_get_eee(dev, useraddr);
2769*4882a593Smuzhiyun break;
2770*4882a593Smuzhiyun case ETHTOOL_SEEE:
2771*4882a593Smuzhiyun rc = ethtool_set_eee(dev, useraddr);
2772*4882a593Smuzhiyun break;
2773*4882a593Smuzhiyun case ETHTOOL_NWAY_RST:
2774*4882a593Smuzhiyun rc = ethtool_nway_reset(dev);
2775*4882a593Smuzhiyun break;
2776*4882a593Smuzhiyun case ETHTOOL_GLINK:
2777*4882a593Smuzhiyun rc = ethtool_get_link(dev, useraddr);
2778*4882a593Smuzhiyun break;
2779*4882a593Smuzhiyun case ETHTOOL_GEEPROM:
2780*4882a593Smuzhiyun rc = ethtool_get_eeprom(dev, useraddr);
2781*4882a593Smuzhiyun break;
2782*4882a593Smuzhiyun case ETHTOOL_SEEPROM:
2783*4882a593Smuzhiyun rc = ethtool_set_eeprom(dev, useraddr);
2784*4882a593Smuzhiyun break;
2785*4882a593Smuzhiyun case ETHTOOL_GCOALESCE:
2786*4882a593Smuzhiyun rc = ethtool_get_coalesce(dev, useraddr);
2787*4882a593Smuzhiyun break;
2788*4882a593Smuzhiyun case ETHTOOL_SCOALESCE:
2789*4882a593Smuzhiyun rc = ethtool_set_coalesce(dev, useraddr);
2790*4882a593Smuzhiyun break;
2791*4882a593Smuzhiyun case ETHTOOL_GRINGPARAM:
2792*4882a593Smuzhiyun rc = ethtool_get_ringparam(dev, useraddr);
2793*4882a593Smuzhiyun break;
2794*4882a593Smuzhiyun case ETHTOOL_SRINGPARAM:
2795*4882a593Smuzhiyun rc = ethtool_set_ringparam(dev, useraddr);
2796*4882a593Smuzhiyun break;
2797*4882a593Smuzhiyun case ETHTOOL_GPAUSEPARAM:
2798*4882a593Smuzhiyun rc = ethtool_get_pauseparam(dev, useraddr);
2799*4882a593Smuzhiyun break;
2800*4882a593Smuzhiyun case ETHTOOL_SPAUSEPARAM:
2801*4882a593Smuzhiyun rc = ethtool_set_pauseparam(dev, useraddr);
2802*4882a593Smuzhiyun break;
2803*4882a593Smuzhiyun case ETHTOOL_TEST:
2804*4882a593Smuzhiyun rc = ethtool_self_test(dev, useraddr);
2805*4882a593Smuzhiyun break;
2806*4882a593Smuzhiyun case ETHTOOL_GSTRINGS:
2807*4882a593Smuzhiyun rc = ethtool_get_strings(dev, useraddr);
2808*4882a593Smuzhiyun break;
2809*4882a593Smuzhiyun case ETHTOOL_PHYS_ID:
2810*4882a593Smuzhiyun rc = ethtool_phys_id(dev, useraddr);
2811*4882a593Smuzhiyun break;
2812*4882a593Smuzhiyun case ETHTOOL_GSTATS:
2813*4882a593Smuzhiyun rc = ethtool_get_stats(dev, useraddr);
2814*4882a593Smuzhiyun break;
2815*4882a593Smuzhiyun case ETHTOOL_GPERMADDR:
2816*4882a593Smuzhiyun rc = ethtool_get_perm_addr(dev, useraddr);
2817*4882a593Smuzhiyun break;
2818*4882a593Smuzhiyun case ETHTOOL_GFLAGS:
2819*4882a593Smuzhiyun rc = ethtool_get_value(dev, useraddr, ethcmd,
2820*4882a593Smuzhiyun __ethtool_get_flags);
2821*4882a593Smuzhiyun break;
2822*4882a593Smuzhiyun case ETHTOOL_SFLAGS:
2823*4882a593Smuzhiyun rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags);
2824*4882a593Smuzhiyun break;
2825*4882a593Smuzhiyun case ETHTOOL_GPFLAGS:
2826*4882a593Smuzhiyun rc = ethtool_get_value(dev, useraddr, ethcmd,
2827*4882a593Smuzhiyun dev->ethtool_ops->get_priv_flags);
2828*4882a593Smuzhiyun if (!rc)
2829*4882a593Smuzhiyun ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL);
2830*4882a593Smuzhiyun break;
2831*4882a593Smuzhiyun case ETHTOOL_SPFLAGS:
2832*4882a593Smuzhiyun rc = ethtool_set_value(dev, useraddr,
2833*4882a593Smuzhiyun dev->ethtool_ops->set_priv_flags);
2834*4882a593Smuzhiyun break;
2835*4882a593Smuzhiyun case ETHTOOL_GRXFH:
2836*4882a593Smuzhiyun case ETHTOOL_GRXRINGS:
2837*4882a593Smuzhiyun case ETHTOOL_GRXCLSRLCNT:
2838*4882a593Smuzhiyun case ETHTOOL_GRXCLSRULE:
2839*4882a593Smuzhiyun case ETHTOOL_GRXCLSRLALL:
2840*4882a593Smuzhiyun rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);
2841*4882a593Smuzhiyun break;
2842*4882a593Smuzhiyun case ETHTOOL_SRXFH:
2843*4882a593Smuzhiyun case ETHTOOL_SRXCLSRLDEL:
2844*4882a593Smuzhiyun case ETHTOOL_SRXCLSRLINS:
2845*4882a593Smuzhiyun rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
2846*4882a593Smuzhiyun break;
2847*4882a593Smuzhiyun case ETHTOOL_FLASHDEV:
2848*4882a593Smuzhiyun rc = ethtool_flash_device(dev, useraddr);
2849*4882a593Smuzhiyun break;
2850*4882a593Smuzhiyun case ETHTOOL_RESET:
2851*4882a593Smuzhiyun rc = ethtool_reset(dev, useraddr);
2852*4882a593Smuzhiyun break;
2853*4882a593Smuzhiyun case ETHTOOL_GSSET_INFO:
2854*4882a593Smuzhiyun rc = ethtool_get_sset_info(dev, useraddr);
2855*4882a593Smuzhiyun break;
2856*4882a593Smuzhiyun case ETHTOOL_GRXFHINDIR:
2857*4882a593Smuzhiyun rc = ethtool_get_rxfh_indir(dev, useraddr);
2858*4882a593Smuzhiyun break;
2859*4882a593Smuzhiyun case ETHTOOL_SRXFHINDIR:
2860*4882a593Smuzhiyun rc = ethtool_set_rxfh_indir(dev, useraddr);
2861*4882a593Smuzhiyun break;
2862*4882a593Smuzhiyun case ETHTOOL_GRSSH:
2863*4882a593Smuzhiyun rc = ethtool_get_rxfh(dev, useraddr);
2864*4882a593Smuzhiyun break;
2865*4882a593Smuzhiyun case ETHTOOL_SRSSH:
2866*4882a593Smuzhiyun rc = ethtool_set_rxfh(dev, useraddr);
2867*4882a593Smuzhiyun break;
2868*4882a593Smuzhiyun case ETHTOOL_GFEATURES:
2869*4882a593Smuzhiyun rc = ethtool_get_features(dev, useraddr);
2870*4882a593Smuzhiyun break;
2871*4882a593Smuzhiyun case ETHTOOL_SFEATURES:
2872*4882a593Smuzhiyun rc = ethtool_set_features(dev, useraddr);
2873*4882a593Smuzhiyun break;
2874*4882a593Smuzhiyun case ETHTOOL_GTXCSUM:
2875*4882a593Smuzhiyun case ETHTOOL_GRXCSUM:
2876*4882a593Smuzhiyun case ETHTOOL_GSG:
2877*4882a593Smuzhiyun case ETHTOOL_GTSO:
2878*4882a593Smuzhiyun case ETHTOOL_GGSO:
2879*4882a593Smuzhiyun case ETHTOOL_GGRO:
2880*4882a593Smuzhiyun rc = ethtool_get_one_feature(dev, useraddr, ethcmd);
2881*4882a593Smuzhiyun break;
2882*4882a593Smuzhiyun case ETHTOOL_STXCSUM:
2883*4882a593Smuzhiyun case ETHTOOL_SRXCSUM:
2884*4882a593Smuzhiyun case ETHTOOL_SSG:
2885*4882a593Smuzhiyun case ETHTOOL_STSO:
2886*4882a593Smuzhiyun case ETHTOOL_SGSO:
2887*4882a593Smuzhiyun case ETHTOOL_SGRO:
2888*4882a593Smuzhiyun rc = ethtool_set_one_feature(dev, useraddr, ethcmd);
2889*4882a593Smuzhiyun break;
2890*4882a593Smuzhiyun case ETHTOOL_GCHANNELS:
2891*4882a593Smuzhiyun rc = ethtool_get_channels(dev, useraddr);
2892*4882a593Smuzhiyun break;
2893*4882a593Smuzhiyun case ETHTOOL_SCHANNELS:
2894*4882a593Smuzhiyun rc = ethtool_set_channels(dev, useraddr);
2895*4882a593Smuzhiyun break;
2896*4882a593Smuzhiyun case ETHTOOL_SET_DUMP:
2897*4882a593Smuzhiyun rc = ethtool_set_dump(dev, useraddr);
2898*4882a593Smuzhiyun break;
2899*4882a593Smuzhiyun case ETHTOOL_GET_DUMP_FLAG:
2900*4882a593Smuzhiyun rc = ethtool_get_dump_flag(dev, useraddr);
2901*4882a593Smuzhiyun break;
2902*4882a593Smuzhiyun case ETHTOOL_GET_DUMP_DATA:
2903*4882a593Smuzhiyun rc = ethtool_get_dump_data(dev, useraddr);
2904*4882a593Smuzhiyun break;
2905*4882a593Smuzhiyun case ETHTOOL_GET_TS_INFO:
2906*4882a593Smuzhiyun rc = ethtool_get_ts_info(dev, useraddr);
2907*4882a593Smuzhiyun break;
2908*4882a593Smuzhiyun case ETHTOOL_GMODULEINFO:
2909*4882a593Smuzhiyun rc = ethtool_get_module_info(dev, useraddr);
2910*4882a593Smuzhiyun break;
2911*4882a593Smuzhiyun case ETHTOOL_GMODULEEEPROM:
2912*4882a593Smuzhiyun rc = ethtool_get_module_eeprom(dev, useraddr);
2913*4882a593Smuzhiyun break;
2914*4882a593Smuzhiyun case ETHTOOL_GTUNABLE:
2915*4882a593Smuzhiyun rc = ethtool_get_tunable(dev, useraddr);
2916*4882a593Smuzhiyun break;
2917*4882a593Smuzhiyun case ETHTOOL_STUNABLE:
2918*4882a593Smuzhiyun rc = ethtool_set_tunable(dev, useraddr);
2919*4882a593Smuzhiyun break;
2920*4882a593Smuzhiyun case ETHTOOL_GPHYSTATS:
2921*4882a593Smuzhiyun rc = ethtool_get_phy_stats(dev, useraddr);
2922*4882a593Smuzhiyun break;
2923*4882a593Smuzhiyun case ETHTOOL_PERQUEUE:
2924*4882a593Smuzhiyun rc = ethtool_set_per_queue(dev, useraddr, sub_cmd);
2925*4882a593Smuzhiyun break;
2926*4882a593Smuzhiyun case ETHTOOL_GLINKSETTINGS:
2927*4882a593Smuzhiyun rc = ethtool_get_link_ksettings(dev, useraddr);
2928*4882a593Smuzhiyun break;
2929*4882a593Smuzhiyun case ETHTOOL_SLINKSETTINGS:
2930*4882a593Smuzhiyun rc = ethtool_set_link_ksettings(dev, useraddr);
2931*4882a593Smuzhiyun break;
2932*4882a593Smuzhiyun case ETHTOOL_PHY_GTUNABLE:
2933*4882a593Smuzhiyun rc = get_phy_tunable(dev, useraddr);
2934*4882a593Smuzhiyun break;
2935*4882a593Smuzhiyun case ETHTOOL_PHY_STUNABLE:
2936*4882a593Smuzhiyun rc = set_phy_tunable(dev, useraddr);
2937*4882a593Smuzhiyun break;
2938*4882a593Smuzhiyun case ETHTOOL_GFECPARAM:
2939*4882a593Smuzhiyun rc = ethtool_get_fecparam(dev, useraddr);
2940*4882a593Smuzhiyun break;
2941*4882a593Smuzhiyun case ETHTOOL_SFECPARAM:
2942*4882a593Smuzhiyun rc = ethtool_set_fecparam(dev, useraddr);
2943*4882a593Smuzhiyun break;
2944*4882a593Smuzhiyun default:
2945*4882a593Smuzhiyun rc = -EOPNOTSUPP;
2946*4882a593Smuzhiyun }
2947*4882a593Smuzhiyun
2948*4882a593Smuzhiyun if (dev->ethtool_ops->complete)
2949*4882a593Smuzhiyun dev->ethtool_ops->complete(dev);
2950*4882a593Smuzhiyun
2951*4882a593Smuzhiyun if (old_features != dev->features)
2952*4882a593Smuzhiyun netdev_features_change(dev);
2953*4882a593Smuzhiyun
2954*4882a593Smuzhiyun return rc;
2955*4882a593Smuzhiyun }
2956*4882a593Smuzhiyun
2957*4882a593Smuzhiyun struct ethtool_rx_flow_key {
2958*4882a593Smuzhiyun struct flow_dissector_key_basic basic;
2959*4882a593Smuzhiyun union {
2960*4882a593Smuzhiyun struct flow_dissector_key_ipv4_addrs ipv4;
2961*4882a593Smuzhiyun struct flow_dissector_key_ipv6_addrs ipv6;
2962*4882a593Smuzhiyun };
2963*4882a593Smuzhiyun struct flow_dissector_key_ports tp;
2964*4882a593Smuzhiyun struct flow_dissector_key_ip ip;
2965*4882a593Smuzhiyun struct flow_dissector_key_vlan vlan;
2966*4882a593Smuzhiyun struct flow_dissector_key_eth_addrs eth_addrs;
2967*4882a593Smuzhiyun } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
2968*4882a593Smuzhiyun
2969*4882a593Smuzhiyun struct ethtool_rx_flow_match {
2970*4882a593Smuzhiyun struct flow_dissector dissector;
2971*4882a593Smuzhiyun struct ethtool_rx_flow_key key;
2972*4882a593Smuzhiyun struct ethtool_rx_flow_key mask;
2973*4882a593Smuzhiyun };
2974*4882a593Smuzhiyun
2975*4882a593Smuzhiyun struct ethtool_rx_flow_rule *
ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input * input)2976*4882a593Smuzhiyun ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input)
2977*4882a593Smuzhiyun {
2978*4882a593Smuzhiyun const struct ethtool_rx_flow_spec *fs = input->fs;
2979*4882a593Smuzhiyun static struct in6_addr zero_addr = {};
2980*4882a593Smuzhiyun struct ethtool_rx_flow_match *match;
2981*4882a593Smuzhiyun struct ethtool_rx_flow_rule *flow;
2982*4882a593Smuzhiyun struct flow_action_entry *act;
2983*4882a593Smuzhiyun
2984*4882a593Smuzhiyun flow = kzalloc(sizeof(struct ethtool_rx_flow_rule) +
2985*4882a593Smuzhiyun sizeof(struct ethtool_rx_flow_match), GFP_KERNEL);
2986*4882a593Smuzhiyun if (!flow)
2987*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
2988*4882a593Smuzhiyun
2989*4882a593Smuzhiyun /* ethtool_rx supports only one single action per rule. */
2990*4882a593Smuzhiyun flow->rule = flow_rule_alloc(1);
2991*4882a593Smuzhiyun if (!flow->rule) {
2992*4882a593Smuzhiyun kfree(flow);
2993*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
2994*4882a593Smuzhiyun }
2995*4882a593Smuzhiyun
2996*4882a593Smuzhiyun match = (struct ethtool_rx_flow_match *)flow->priv;
2997*4882a593Smuzhiyun flow->rule->match.dissector = &match->dissector;
2998*4882a593Smuzhiyun flow->rule->match.mask = &match->mask;
2999*4882a593Smuzhiyun flow->rule->match.key = &match->key;
3000*4882a593Smuzhiyun
3001*4882a593Smuzhiyun match->mask.basic.n_proto = htons(0xffff);
3002*4882a593Smuzhiyun
3003*4882a593Smuzhiyun switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
3004*4882a593Smuzhiyun case ETHER_FLOW: {
3005*4882a593Smuzhiyun const struct ethhdr *ether_spec, *ether_m_spec;
3006*4882a593Smuzhiyun
3007*4882a593Smuzhiyun ether_spec = &fs->h_u.ether_spec;
3008*4882a593Smuzhiyun ether_m_spec = &fs->m_u.ether_spec;
3009*4882a593Smuzhiyun
3010*4882a593Smuzhiyun if (!is_zero_ether_addr(ether_m_spec->h_source)) {
3011*4882a593Smuzhiyun ether_addr_copy(match->key.eth_addrs.src,
3012*4882a593Smuzhiyun ether_spec->h_source);
3013*4882a593Smuzhiyun ether_addr_copy(match->mask.eth_addrs.src,
3014*4882a593Smuzhiyun ether_m_spec->h_source);
3015*4882a593Smuzhiyun }
3016*4882a593Smuzhiyun if (!is_zero_ether_addr(ether_m_spec->h_dest)) {
3017*4882a593Smuzhiyun ether_addr_copy(match->key.eth_addrs.dst,
3018*4882a593Smuzhiyun ether_spec->h_dest);
3019*4882a593Smuzhiyun ether_addr_copy(match->mask.eth_addrs.dst,
3020*4882a593Smuzhiyun ether_m_spec->h_dest);
3021*4882a593Smuzhiyun }
3022*4882a593Smuzhiyun if (ether_m_spec->h_proto) {
3023*4882a593Smuzhiyun match->key.basic.n_proto = ether_spec->h_proto;
3024*4882a593Smuzhiyun match->mask.basic.n_proto = ether_m_spec->h_proto;
3025*4882a593Smuzhiyun }
3026*4882a593Smuzhiyun }
3027*4882a593Smuzhiyun break;
3028*4882a593Smuzhiyun case TCP_V4_FLOW:
3029*4882a593Smuzhiyun case UDP_V4_FLOW: {
3030*4882a593Smuzhiyun const struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec;
3031*4882a593Smuzhiyun
3032*4882a593Smuzhiyun match->key.basic.n_proto = htons(ETH_P_IP);
3033*4882a593Smuzhiyun
3034*4882a593Smuzhiyun v4_spec = &fs->h_u.tcp_ip4_spec;
3035*4882a593Smuzhiyun v4_m_spec = &fs->m_u.tcp_ip4_spec;
3036*4882a593Smuzhiyun
3037*4882a593Smuzhiyun if (v4_m_spec->ip4src) {
3038*4882a593Smuzhiyun match->key.ipv4.src = v4_spec->ip4src;
3039*4882a593Smuzhiyun match->mask.ipv4.src = v4_m_spec->ip4src;
3040*4882a593Smuzhiyun }
3041*4882a593Smuzhiyun if (v4_m_spec->ip4dst) {
3042*4882a593Smuzhiyun match->key.ipv4.dst = v4_spec->ip4dst;
3043*4882a593Smuzhiyun match->mask.ipv4.dst = v4_m_spec->ip4dst;
3044*4882a593Smuzhiyun }
3045*4882a593Smuzhiyun if (v4_m_spec->ip4src ||
3046*4882a593Smuzhiyun v4_m_spec->ip4dst) {
3047*4882a593Smuzhiyun match->dissector.used_keys |=
3048*4882a593Smuzhiyun BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
3049*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_IPV4_ADDRS] =
3050*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, ipv4);
3051*4882a593Smuzhiyun }
3052*4882a593Smuzhiyun if (v4_m_spec->psrc) {
3053*4882a593Smuzhiyun match->key.tp.src = v4_spec->psrc;
3054*4882a593Smuzhiyun match->mask.tp.src = v4_m_spec->psrc;
3055*4882a593Smuzhiyun }
3056*4882a593Smuzhiyun if (v4_m_spec->pdst) {
3057*4882a593Smuzhiyun match->key.tp.dst = v4_spec->pdst;
3058*4882a593Smuzhiyun match->mask.tp.dst = v4_m_spec->pdst;
3059*4882a593Smuzhiyun }
3060*4882a593Smuzhiyun if (v4_m_spec->psrc ||
3061*4882a593Smuzhiyun v4_m_spec->pdst) {
3062*4882a593Smuzhiyun match->dissector.used_keys |=
3063*4882a593Smuzhiyun BIT(FLOW_DISSECTOR_KEY_PORTS);
3064*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
3065*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, tp);
3066*4882a593Smuzhiyun }
3067*4882a593Smuzhiyun if (v4_m_spec->tos) {
3068*4882a593Smuzhiyun match->key.ip.tos = v4_spec->tos;
3069*4882a593Smuzhiyun match->mask.ip.tos = v4_m_spec->tos;
3070*4882a593Smuzhiyun match->dissector.used_keys |=
3071*4882a593Smuzhiyun BIT(FLOW_DISSECTOR_KEY_IP);
3072*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
3073*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, ip);
3074*4882a593Smuzhiyun }
3075*4882a593Smuzhiyun }
3076*4882a593Smuzhiyun break;
3077*4882a593Smuzhiyun case TCP_V6_FLOW:
3078*4882a593Smuzhiyun case UDP_V6_FLOW: {
3079*4882a593Smuzhiyun const struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec;
3080*4882a593Smuzhiyun
3081*4882a593Smuzhiyun match->key.basic.n_proto = htons(ETH_P_IPV6);
3082*4882a593Smuzhiyun
3083*4882a593Smuzhiyun v6_spec = &fs->h_u.tcp_ip6_spec;
3084*4882a593Smuzhiyun v6_m_spec = &fs->m_u.tcp_ip6_spec;
3085*4882a593Smuzhiyun if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
3086*4882a593Smuzhiyun memcpy(&match->key.ipv6.src, v6_spec->ip6src,
3087*4882a593Smuzhiyun sizeof(match->key.ipv6.src));
3088*4882a593Smuzhiyun memcpy(&match->mask.ipv6.src, v6_m_spec->ip6src,
3089*4882a593Smuzhiyun sizeof(match->mask.ipv6.src));
3090*4882a593Smuzhiyun }
3091*4882a593Smuzhiyun if (memcmp(v6_m_spec->ip6dst, &zero_addr, sizeof(zero_addr))) {
3092*4882a593Smuzhiyun memcpy(&match->key.ipv6.dst, v6_spec->ip6dst,
3093*4882a593Smuzhiyun sizeof(match->key.ipv6.dst));
3094*4882a593Smuzhiyun memcpy(&match->mask.ipv6.dst, v6_m_spec->ip6dst,
3095*4882a593Smuzhiyun sizeof(match->mask.ipv6.dst));
3096*4882a593Smuzhiyun }
3097*4882a593Smuzhiyun if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr)) ||
3098*4882a593Smuzhiyun memcmp(v6_m_spec->ip6dst, &zero_addr, sizeof(zero_addr))) {
3099*4882a593Smuzhiyun match->dissector.used_keys |=
3100*4882a593Smuzhiyun BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
3101*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_IPV6_ADDRS] =
3102*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, ipv6);
3103*4882a593Smuzhiyun }
3104*4882a593Smuzhiyun if (v6_m_spec->psrc) {
3105*4882a593Smuzhiyun match->key.tp.src = v6_spec->psrc;
3106*4882a593Smuzhiyun match->mask.tp.src = v6_m_spec->psrc;
3107*4882a593Smuzhiyun }
3108*4882a593Smuzhiyun if (v6_m_spec->pdst) {
3109*4882a593Smuzhiyun match->key.tp.dst = v6_spec->pdst;
3110*4882a593Smuzhiyun match->mask.tp.dst = v6_m_spec->pdst;
3111*4882a593Smuzhiyun }
3112*4882a593Smuzhiyun if (v6_m_spec->psrc ||
3113*4882a593Smuzhiyun v6_m_spec->pdst) {
3114*4882a593Smuzhiyun match->dissector.used_keys |=
3115*4882a593Smuzhiyun BIT(FLOW_DISSECTOR_KEY_PORTS);
3116*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
3117*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, tp);
3118*4882a593Smuzhiyun }
3119*4882a593Smuzhiyun if (v6_m_spec->tclass) {
3120*4882a593Smuzhiyun match->key.ip.tos = v6_spec->tclass;
3121*4882a593Smuzhiyun match->mask.ip.tos = v6_m_spec->tclass;
3122*4882a593Smuzhiyun match->dissector.used_keys |=
3123*4882a593Smuzhiyun BIT(FLOW_DISSECTOR_KEY_IP);
3124*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
3125*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, ip);
3126*4882a593Smuzhiyun }
3127*4882a593Smuzhiyun }
3128*4882a593Smuzhiyun break;
3129*4882a593Smuzhiyun default:
3130*4882a593Smuzhiyun ethtool_rx_flow_rule_destroy(flow);
3131*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
3132*4882a593Smuzhiyun }
3133*4882a593Smuzhiyun
3134*4882a593Smuzhiyun switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
3135*4882a593Smuzhiyun case TCP_V4_FLOW:
3136*4882a593Smuzhiyun case TCP_V6_FLOW:
3137*4882a593Smuzhiyun match->key.basic.ip_proto = IPPROTO_TCP;
3138*4882a593Smuzhiyun match->mask.basic.ip_proto = 0xff;
3139*4882a593Smuzhiyun break;
3140*4882a593Smuzhiyun case UDP_V4_FLOW:
3141*4882a593Smuzhiyun case UDP_V6_FLOW:
3142*4882a593Smuzhiyun match->key.basic.ip_proto = IPPROTO_UDP;
3143*4882a593Smuzhiyun match->mask.basic.ip_proto = 0xff;
3144*4882a593Smuzhiyun break;
3145*4882a593Smuzhiyun }
3146*4882a593Smuzhiyun
3147*4882a593Smuzhiyun match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
3148*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_BASIC] =
3149*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, basic);
3150*4882a593Smuzhiyun
3151*4882a593Smuzhiyun if (fs->flow_type & FLOW_EXT) {
3152*4882a593Smuzhiyun const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
3153*4882a593Smuzhiyun const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
3154*4882a593Smuzhiyun
3155*4882a593Smuzhiyun if (ext_m_spec->vlan_etype) {
3156*4882a593Smuzhiyun match->key.vlan.vlan_tpid = ext_h_spec->vlan_etype;
3157*4882a593Smuzhiyun match->mask.vlan.vlan_tpid = ext_m_spec->vlan_etype;
3158*4882a593Smuzhiyun }
3159*4882a593Smuzhiyun
3160*4882a593Smuzhiyun if (ext_m_spec->vlan_tci) {
3161*4882a593Smuzhiyun match->key.vlan.vlan_id =
3162*4882a593Smuzhiyun ntohs(ext_h_spec->vlan_tci) & 0x0fff;
3163*4882a593Smuzhiyun match->mask.vlan.vlan_id =
3164*4882a593Smuzhiyun ntohs(ext_m_spec->vlan_tci) & 0x0fff;
3165*4882a593Smuzhiyun
3166*4882a593Smuzhiyun match->key.vlan.vlan_dei =
3167*4882a593Smuzhiyun !!(ext_h_spec->vlan_tci & htons(0x1000));
3168*4882a593Smuzhiyun match->mask.vlan.vlan_dei =
3169*4882a593Smuzhiyun !!(ext_m_spec->vlan_tci & htons(0x1000));
3170*4882a593Smuzhiyun
3171*4882a593Smuzhiyun match->key.vlan.vlan_priority =
3172*4882a593Smuzhiyun (ntohs(ext_h_spec->vlan_tci) & 0xe000) >> 13;
3173*4882a593Smuzhiyun match->mask.vlan.vlan_priority =
3174*4882a593Smuzhiyun (ntohs(ext_m_spec->vlan_tci) & 0xe000) >> 13;
3175*4882a593Smuzhiyun }
3176*4882a593Smuzhiyun
3177*4882a593Smuzhiyun if (ext_m_spec->vlan_etype ||
3178*4882a593Smuzhiyun ext_m_spec->vlan_tci) {
3179*4882a593Smuzhiyun match->dissector.used_keys |=
3180*4882a593Smuzhiyun BIT(FLOW_DISSECTOR_KEY_VLAN);
3181*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] =
3182*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, vlan);
3183*4882a593Smuzhiyun }
3184*4882a593Smuzhiyun }
3185*4882a593Smuzhiyun if (fs->flow_type & FLOW_MAC_EXT) {
3186*4882a593Smuzhiyun const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext;
3187*4882a593Smuzhiyun const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext;
3188*4882a593Smuzhiyun
3189*4882a593Smuzhiyun memcpy(match->key.eth_addrs.dst, ext_h_spec->h_dest,
3190*4882a593Smuzhiyun ETH_ALEN);
3191*4882a593Smuzhiyun memcpy(match->mask.eth_addrs.dst, ext_m_spec->h_dest,
3192*4882a593Smuzhiyun ETH_ALEN);
3193*4882a593Smuzhiyun
3194*4882a593Smuzhiyun match->dissector.used_keys |=
3195*4882a593Smuzhiyun BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
3196*4882a593Smuzhiyun match->dissector.offset[FLOW_DISSECTOR_KEY_ETH_ADDRS] =
3197*4882a593Smuzhiyun offsetof(struct ethtool_rx_flow_key, eth_addrs);
3198*4882a593Smuzhiyun }
3199*4882a593Smuzhiyun
3200*4882a593Smuzhiyun act = &flow->rule->action.entries[0];
3201*4882a593Smuzhiyun switch (fs->ring_cookie) {
3202*4882a593Smuzhiyun case RX_CLS_FLOW_DISC:
3203*4882a593Smuzhiyun act->id = FLOW_ACTION_DROP;
3204*4882a593Smuzhiyun break;
3205*4882a593Smuzhiyun case RX_CLS_FLOW_WAKE:
3206*4882a593Smuzhiyun act->id = FLOW_ACTION_WAKE;
3207*4882a593Smuzhiyun break;
3208*4882a593Smuzhiyun default:
3209*4882a593Smuzhiyun act->id = FLOW_ACTION_QUEUE;
3210*4882a593Smuzhiyun if (fs->flow_type & FLOW_RSS)
3211*4882a593Smuzhiyun act->queue.ctx = input->rss_ctx;
3212*4882a593Smuzhiyun
3213*4882a593Smuzhiyun act->queue.vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
3214*4882a593Smuzhiyun act->queue.index = ethtool_get_flow_spec_ring(fs->ring_cookie);
3215*4882a593Smuzhiyun break;
3216*4882a593Smuzhiyun }
3217*4882a593Smuzhiyun
3218*4882a593Smuzhiyun return flow;
3219*4882a593Smuzhiyun }
3220*4882a593Smuzhiyun EXPORT_SYMBOL(ethtool_rx_flow_rule_create);
3221*4882a593Smuzhiyun
ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule * flow)3222*4882a593Smuzhiyun void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *flow)
3223*4882a593Smuzhiyun {
3224*4882a593Smuzhiyun kfree(flow->rule);
3225*4882a593Smuzhiyun kfree(flow);
3226*4882a593Smuzhiyun }
3227*4882a593Smuzhiyun EXPORT_SYMBOL(ethtool_rx_flow_rule_destroy);
3228