blob: 65b3d50a6df2377f121e7eca9f7dc72b17fc7c59 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * net/core/ethtool.c - Ethtool ioctl handler
3 * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
4 *
5 * This file is where we call all the ethtool_ops commands to get
Matthew Wilcox61a44b92007-07-31 14:00:02 -07006 * the information ethtool needs.
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Matthew Wilcox61a44b92007-07-31 14:00:02 -07008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
Linus Torvalds1da177e2005-04-16 15:20:36 -070012 */
13
14#include <linux/module.h>
15#include <linux/types.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080016#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/errno.h>
18#include <linux/ethtool.h>
19#include <linux/netdevice.h>
Jeff Garzikd17792e2010-03-04 08:21:53 +000020#include <linux/bitops.h>
chavey97f8aef2010-04-07 21:54:42 -070021#include <linux/uaccess.h>
David S. Miller73da16c2010-09-21 16:12:11 -070022#include <linux/vmalloc.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090023#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090025/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 * Some useful ethtool_ops methods that're device independent.
27 * If we find that all drivers want to do the same thing here,
28 * we can turn these into dev_() function calls.
29 */
30
31u32 ethtool_op_get_link(struct net_device *dev)
32{
33 return netif_carrier_ok(dev) ? 1 : 0;
34}
chavey97f8aef2010-04-07 21:54:42 -070035EXPORT_SYMBOL(ethtool_op_get_link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
Sridhar Samudrala1896e612009-07-22 13:38:22 +000037u32 ethtool_op_get_rx_csum(struct net_device *dev)
38{
39 return (dev->features & NETIF_F_ALL_CSUM) != 0;
40}
Eric Dumazet8a729fc2009-07-26 23:18:11 +000041EXPORT_SYMBOL(ethtool_op_get_rx_csum);
Sridhar Samudrala1896e612009-07-22 13:38:22 +000042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043u32 ethtool_op_get_tx_csum(struct net_device *dev)
44{
Herbert Xu8648b302006-06-17 22:06:05 -070045 return (dev->features & NETIF_F_ALL_CSUM) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046}
Eric Dumazet8a729fc2009-07-26 23:18:11 +000047EXPORT_SYMBOL(ethtool_op_get_tx_csum);
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
49int ethtool_op_set_tx_csum(struct net_device *dev, u32 data)
50{
51 if (data)
52 dev->features |= NETIF_F_IP_CSUM;
53 else
54 dev->features &= ~NETIF_F_IP_CSUM;
55
56 return 0;
57}
Michał Mirosław9a279ea2011-02-15 16:59:16 +000058EXPORT_SYMBOL(ethtool_op_set_tx_csum);
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Jon Mason69f6a0f2005-05-29 20:27:24 -070060int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data)
61{
62 if (data)
63 dev->features |= NETIF_F_HW_CSUM;
64 else
65 dev->features &= ~NETIF_F_HW_CSUM;
66
67 return 0;
68}
chavey97f8aef2010-04-07 21:54:42 -070069EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
Michael Chan6460d942007-07-14 19:07:52 -070070
71int ethtool_op_set_tx_ipv6_csum(struct net_device *dev, u32 data)
72{
73 if (data)
74 dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
75 else
76 dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
77
78 return 0;
79}
chavey97f8aef2010-04-07 21:54:42 -070080EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum);
Michael Chan6460d942007-07-14 19:07:52 -070081
Linus Torvalds1da177e2005-04-16 15:20:36 -070082u32 ethtool_op_get_sg(struct net_device *dev)
83{
84 return (dev->features & NETIF_F_SG) != 0;
85}
chavey97f8aef2010-04-07 21:54:42 -070086EXPORT_SYMBOL(ethtool_op_get_sg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
88int ethtool_op_set_sg(struct net_device *dev, u32 data)
89{
90 if (data)
91 dev->features |= NETIF_F_SG;
92 else
93 dev->features &= ~NETIF_F_SG;
94
95 return 0;
96}
chavey97f8aef2010-04-07 21:54:42 -070097EXPORT_SYMBOL(ethtool_op_set_sg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
99u32 ethtool_op_get_tso(struct net_device *dev)
100{
101 return (dev->features & NETIF_F_TSO) != 0;
102}
chavey97f8aef2010-04-07 21:54:42 -0700103EXPORT_SYMBOL(ethtool_op_get_tso);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
105int ethtool_op_set_tso(struct net_device *dev, u32 data)
106{
107 if (data)
108 dev->features |= NETIF_F_TSO;
109 else
110 dev->features &= ~NETIF_F_TSO;
111
112 return 0;
113}
chavey97f8aef2010-04-07 21:54:42 -0700114EXPORT_SYMBOL(ethtool_op_set_tso);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700116u32 ethtool_op_get_ufo(struct net_device *dev)
117{
118 return (dev->features & NETIF_F_UFO) != 0;
119}
chavey97f8aef2010-04-07 21:54:42 -0700120EXPORT_SYMBOL(ethtool_op_get_ufo);
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700121
122int ethtool_op_set_ufo(struct net_device *dev, u32 data)
123{
124 if (data)
125 dev->features |= NETIF_F_UFO;
126 else
127 dev->features &= ~NETIF_F_UFO;
128 return 0;
129}
chavey97f8aef2010-04-07 21:54:42 -0700130EXPORT_SYMBOL(ethtool_op_set_ufo);
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700131
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -0700132/* the following list of flags are the same as their associated
133 * NETIF_F_xxx values in include/linux/netdevice.h
134 */
135static const u32 flags_dup_features =
Jesse Grossd5dbda22010-10-20 13:56:07 +0000136 (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE |
137 ETH_FLAG_RXHASH);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -0700138
139u32 ethtool_op_get_flags(struct net_device *dev)
140{
141 /* in the future, this function will probably contain additional
142 * handling for flags which are not so easily handled
143 * by a simple masking operation
144 */
145
146 return dev->features & flags_dup_features;
147}
chavey97f8aef2010-04-07 21:54:42 -0700148EXPORT_SYMBOL(ethtool_op_get_flags);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -0700149
Ben Hutchings1437ce32010-06-30 02:44:32 +0000150int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported)
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -0700151{
Ben Hutchings1437ce32010-06-30 02:44:32 +0000152 if (data & ~supported)
153 return -EINVAL;
Peter Waskiewicz0d643e12010-02-12 13:48:25 +0000154
Ben Hutchings1437ce32010-06-30 02:44:32 +0000155 dev->features = ((dev->features & ~flags_dup_features) |
156 (data & flags_dup_features));
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -0700157 return 0;
158}
chavey97f8aef2010-04-07 21:54:42 -0700159EXPORT_SYMBOL(ethtool_op_set_flags);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -0700160
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800161void ethtool_ntuple_flush(struct net_device *dev)
162{
163 struct ethtool_rx_ntuple_flow_spec_container *fsc, *f;
164
165 list_for_each_entry_safe(fsc, f, &dev->ethtool_ntuple_list.list, list) {
166 list_del(&fsc->list);
167 kfree(fsc);
168 }
169 dev->ethtool_ntuple_list.count = 0;
170}
171EXPORT_SYMBOL(ethtool_ntuple_flush);
172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173/* Handlers for each ethtool command */
174
Michał Mirosław5455c692011-02-15 16:59:17 +0000175#define ETHTOOL_DEV_FEATURE_WORDS 1
176
177static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
178{
179 struct ethtool_gfeatures cmd = {
180 .cmd = ETHTOOL_GFEATURES,
181 .size = ETHTOOL_DEV_FEATURE_WORDS,
182 };
183 struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS] = {
184 {
185 .available = dev->hw_features,
186 .requested = dev->wanted_features,
187 .active = dev->features,
188 .never_changed = NETIF_F_NEVER_CHANGE,
189 },
190 };
191 u32 __user *sizeaddr;
192 u32 copy_size;
193
194 sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
195 if (get_user(copy_size, sizeaddr))
196 return -EFAULT;
197
198 if (copy_size > ETHTOOL_DEV_FEATURE_WORDS)
199 copy_size = ETHTOOL_DEV_FEATURE_WORDS;
200
201 if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
202 return -EFAULT;
203 useraddr += sizeof(cmd);
204 if (copy_to_user(useraddr, features, copy_size * sizeof(*features)))
205 return -EFAULT;
206
207 return 0;
208}
209
210static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
211{
212 struct ethtool_sfeatures cmd;
213 struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
214 int ret = 0;
215
216 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
217 return -EFAULT;
218 useraddr += sizeof(cmd);
219
220 if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS)
221 return -EINVAL;
222
223 if (copy_from_user(features, useraddr, sizeof(features)))
224 return -EFAULT;
225
226 if (features[0].valid & ~NETIF_F_ETHTOOL_BITS)
227 return -EINVAL;
228
229 if (features[0].valid & ~dev->hw_features) {
230 features[0].valid &= dev->hw_features;
231 ret |= ETHTOOL_F_UNSUPPORTED;
232 }
233
234 dev->wanted_features &= ~features[0].valid;
235 dev->wanted_features |= features[0].valid & features[0].requested;
236 netdev_update_features(dev);
237
238 if ((dev->wanted_features ^ dev->features) & features[0].valid)
239 ret |= ETHTOOL_F_WISH;
240
241 return ret;
242}
243
244static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GSTRING_LEN] = {
245 /* NETIF_F_SG */ "tx-scatter-gather",
246 /* NETIF_F_IP_CSUM */ "tx-checksum-ipv4",
247 /* NETIF_F_NO_CSUM */ "tx-checksum-unneeded",
248 /* NETIF_F_HW_CSUM */ "tx-checksum-ip-generic",
249 /* NETIF_F_IPV6_CSUM */ "tx_checksum-ipv6",
250 /* NETIF_F_HIGHDMA */ "highdma",
251 /* NETIF_F_FRAGLIST */ "tx-scatter-gather-fraglist",
252 /* NETIF_F_HW_VLAN_TX */ "tx-vlan-hw-insert",
253
254 /* NETIF_F_HW_VLAN_RX */ "rx-vlan-hw-parse",
255 /* NETIF_F_HW_VLAN_FILTER */ "rx-vlan-filter",
256 /* NETIF_F_VLAN_CHALLENGED */ "vlan-challenged",
257 /* NETIF_F_GSO */ "tx-generic-segmentation",
258 /* NETIF_F_LLTX */ "tx-lockless",
259 /* NETIF_F_NETNS_LOCAL */ "netns-local",
260 /* NETIF_F_GRO */ "rx-gro",
261 /* NETIF_F_LRO */ "rx-lro",
262
263 /* NETIF_F_TSO */ "tx-tcp-segmentation",
264 /* NETIF_F_UFO */ "tx-udp-fragmentation",
265 /* NETIF_F_GSO_ROBUST */ "tx-gso-robust",
266 /* NETIF_F_TSO_ECN */ "tx-tcp-ecn-segmentation",
267 /* NETIF_F_TSO6 */ "tx-tcp6-segmentation",
268 /* NETIF_F_FSO */ "tx-fcoe-segmentation",
269 "",
270 "",
271
272 /* NETIF_F_FCOE_CRC */ "tx-checksum-fcoe-crc",
273 /* NETIF_F_SCTP_CSUM */ "tx-checksum-sctp",
274 /* NETIF_F_FCOE_MTU */ "fcoe-mtu",
275 /* NETIF_F_NTUPLE */ "rx-ntuple-filter",
276 /* NETIF_F_RXHASH */ "rx-hashing",
277 "",
278 "",
279 "",
280};
281
Michał Mirosław340ae162011-02-15 16:59:16 +0000282static int __ethtool_get_sset_count(struct net_device *dev, int sset)
283{
284 const struct ethtool_ops *ops = dev->ethtool_ops;
285
Michał Mirosław5455c692011-02-15 16:59:17 +0000286 if (sset == ETH_SS_FEATURES)
287 return ARRAY_SIZE(netdev_features_strings);
288
Michał Mirosław340ae162011-02-15 16:59:16 +0000289 if (ops && ops->get_sset_count && ops->get_strings)
290 return ops->get_sset_count(dev, sset);
291 else
292 return -EOPNOTSUPP;
293}
294
295static void __ethtool_get_strings(struct net_device *dev,
296 u32 stringset, u8 *data)
297{
298 const struct ethtool_ops *ops = dev->ethtool_ops;
299
Michał Mirosław5455c692011-02-15 16:59:17 +0000300 if (stringset == ETH_SS_FEATURES)
301 memcpy(data, netdev_features_strings,
302 sizeof(netdev_features_strings));
303 else
304 /* ops->get_strings is valid because checked earlier */
305 ops->get_strings(dev, stringset, data);
Michał Mirosław340ae162011-02-15 16:59:16 +0000306}
307
Michał Mirosław0a417702011-02-15 16:59:17 +0000308static u32 ethtool_get_feature_mask(u32 eth_cmd)
309{
310 /* feature masks of legacy discrete ethtool ops */
311
312 switch (eth_cmd) {
313 case ETHTOOL_GTXCSUM:
314 case ETHTOOL_STXCSUM:
315 return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM;
316 case ETHTOOL_GSG:
317 case ETHTOOL_SSG:
318 return NETIF_F_SG;
319 case ETHTOOL_GTSO:
320 case ETHTOOL_STSO:
321 return NETIF_F_ALL_TSO;
322 case ETHTOOL_GUFO:
323 case ETHTOOL_SUFO:
324 return NETIF_F_UFO;
325 case ETHTOOL_GGSO:
326 case ETHTOOL_SGSO:
327 return NETIF_F_GSO;
328 case ETHTOOL_GGRO:
329 case ETHTOOL_SGRO:
330 return NETIF_F_GRO;
331 default:
332 BUG();
333 }
334}
335
336static void *__ethtool_get_one_feature_actor(struct net_device *dev, u32 ethcmd)
337{
338 const struct ethtool_ops *ops = dev->ethtool_ops;
339
340 if (!ops)
341 return NULL;
342
343 switch (ethcmd) {
344 case ETHTOOL_GTXCSUM:
345 return ops->get_tx_csum;
346 case ETHTOOL_SSG:
347 return ops->get_sg;
348 case ETHTOOL_STSO:
349 return ops->get_tso;
350 case ETHTOOL_SUFO:
351 return ops->get_ufo;
352 default:
353 return NULL;
354 }
355}
356
357static int ethtool_get_one_feature(struct net_device *dev,
358 char __user *useraddr, u32 ethcmd)
359{
Michał Mirosław86794882011-02-15 16:59:17 +0000360 u32 mask = ethtool_get_feature_mask(ethcmd);
Michał Mirosław0a417702011-02-15 16:59:17 +0000361 struct ethtool_value edata = {
362 .cmd = ethcmd,
Michał Mirosław86794882011-02-15 16:59:17 +0000363 .data = !!(dev->features & mask),
Michał Mirosław0a417702011-02-15 16:59:17 +0000364 };
Michał Mirosław0a417702011-02-15 16:59:17 +0000365
Michał Mirosław86794882011-02-15 16:59:17 +0000366 /* compatibility with discrete get_ ops */
367 if (!(dev->hw_features & mask)) {
368 u32 (*actor)(struct net_device *);
369
370 actor = __ethtool_get_one_feature_actor(dev, ethcmd);
371
372 if (actor)
373 edata.data = actor(dev);
374 }
Michał Mirosław0a417702011-02-15 16:59:17 +0000375
376 if (copy_to_user(useraddr, &edata, sizeof(edata)))
377 return -EFAULT;
378 return 0;
379}
380
381static int __ethtool_set_tx_csum(struct net_device *dev, u32 data);
382static int __ethtool_set_sg(struct net_device *dev, u32 data);
383static int __ethtool_set_tso(struct net_device *dev, u32 data);
384static int __ethtool_set_ufo(struct net_device *dev, u32 data);
385
386static int ethtool_set_one_feature(struct net_device *dev,
387 void __user *useraddr, u32 ethcmd)
388{
389 struct ethtool_value edata;
390 u32 mask;
391
392 if (copy_from_user(&edata, useraddr, sizeof(edata)))
393 return -EFAULT;
394
Michał Mirosław86794882011-02-15 16:59:17 +0000395 mask = ethtool_get_feature_mask(ethcmd);
396 mask &= dev->hw_features;
397 if (mask) {
398 if (edata.data)
399 dev->wanted_features |= mask;
400 else
401 dev->wanted_features &= ~mask;
402
403 netdev_update_features(dev);
404 return 0;
405 }
406
407 /* Driver is not converted to ndo_fix_features or does not
408 * support changing this offload. In the latter case it won't
409 * have corresponding ethtool_ops field set.
410 *
411 * Following part is to be removed after all drivers advertise
412 * their changeable features in netdev->hw_features and stop
413 * using discrete offload setting ops.
414 */
415
Michał Mirosław0a417702011-02-15 16:59:17 +0000416 switch (ethcmd) {
417 case ETHTOOL_STXCSUM:
418 return __ethtool_set_tx_csum(dev, edata.data);
419 case ETHTOOL_SSG:
420 return __ethtool_set_sg(dev, edata.data);
421 case ETHTOOL_STSO:
422 return __ethtool_set_tso(dev, edata.data);
423 case ETHTOOL_SUFO:
424 return __ethtool_set_ufo(dev, edata.data);
Michał Mirosław0a417702011-02-15 16:59:17 +0000425 default:
426 return -EOPNOTSUPP;
427 }
428}
429
Michał Mirosławda8ac86c2011-02-15 16:59:18 +0000430static int __ethtool_set_flags(struct net_device *dev, u32 data)
431{
432 u32 changed;
433
434 if (data & ~flags_dup_features)
435 return -EINVAL;
436
437 /* legacy set_flags() op */
438 if (dev->ethtool_ops->set_flags) {
439 if (unlikely(dev->hw_features & flags_dup_features))
440 netdev_warn(dev,
441 "driver BUG: mixed hw_features and set_flags()\n");
442 return dev->ethtool_ops->set_flags(dev, data);
443 }
444
445 /* allow changing only bits set in hw_features */
446 changed = (data ^ dev->wanted_features) & flags_dup_features;
447 if (changed & ~dev->hw_features)
448 return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP;
449
450 dev->wanted_features =
451 (dev->wanted_features & ~changed) | data;
452
453 netdev_update_features(dev);
454
455 return 0;
456}
457
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
459{
Roland Dreier8e557422010-02-11 12:14:23 -0800460 struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 int err;
462
463 if (!dev->ethtool_ops->get_settings)
464 return -EOPNOTSUPP;
465
466 err = dev->ethtool_ops->get_settings(dev, &cmd);
467 if (err < 0)
468 return err;
469
470 if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
471 return -EFAULT;
472 return 0;
473}
474
475static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
476{
477 struct ethtool_cmd cmd;
478
479 if (!dev->ethtool_ops->set_settings)
480 return -EOPNOTSUPP;
481
482 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
483 return -EFAULT;
484
485 return dev->ethtool_ops->set_settings(dev, &cmd);
486}
487
chavey97f8aef2010-04-07 21:54:42 -0700488static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
489 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
491 struct ethtool_drvinfo info;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700492 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 memset(&info, 0, sizeof(info));
495 info.cmd = ETHTOOL_GDRVINFO;
Ben Hutchings01414802010-08-17 02:31:15 -0700496 if (ops && ops->get_drvinfo) {
497 ops->get_drvinfo(dev, &info);
498 } else if (dev->dev.parent && dev->dev.parent->driver) {
499 strlcpy(info.bus_info, dev_name(dev->dev.parent),
500 sizeof(info.bus_info));
501 strlcpy(info.driver, dev->dev.parent->driver->name,
502 sizeof(info.driver));
503 } else {
504 return -EOPNOTSUPP;
505 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Jeff Garzik723b2f52010-03-03 22:51:50 +0000507 /*
508 * this method of obtaining string set info is deprecated;
Jeff Garzikd17792e2010-03-04 08:21:53 +0000509 * Use ETHTOOL_GSSET_INFO instead.
Jeff Garzik723b2f52010-03-03 22:51:50 +0000510 */
Ben Hutchings01414802010-08-17 02:31:15 -0700511 if (ops && ops->get_sset_count) {
Jeff Garzikff03d492007-08-15 16:01:08 -0700512 int rc;
513
514 rc = ops->get_sset_count(dev, ETH_SS_TEST);
515 if (rc >= 0)
516 info.testinfo_len = rc;
517 rc = ops->get_sset_count(dev, ETH_SS_STATS);
518 if (rc >= 0)
519 info.n_stats = rc;
Jeff Garzik339bf022007-08-15 16:01:32 -0700520 rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
521 if (rc >= 0)
522 info.n_priv_flags = rc;
Jeff Garzikff03d492007-08-15 16:01:08 -0700523 }
Ben Hutchings01414802010-08-17 02:31:15 -0700524 if (ops && ops->get_regs_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 info.regdump_len = ops->get_regs_len(dev);
Ben Hutchings01414802010-08-17 02:31:15 -0700526 if (ops && ops->get_eeprom_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 info.eedump_len = ops->get_eeprom_len(dev);
528
529 if (copy_to_user(useraddr, &info, sizeof(info)))
530 return -EFAULT;
531 return 0;
532}
533
Eric Dumazetf5c445e2010-03-08 12:17:04 -0800534static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
chavey97f8aef2010-04-07 21:54:42 -0700535 void __user *useraddr)
Jeff Garzik723b2f52010-03-03 22:51:50 +0000536{
537 struct ethtool_sset_info info;
Jeff Garzik723b2f52010-03-03 22:51:50 +0000538 u64 sset_mask;
539 int i, idx = 0, n_bits = 0, ret, rc;
540 u32 *info_buf = NULL;
541
Jeff Garzik723b2f52010-03-03 22:51:50 +0000542 if (copy_from_user(&info, useraddr, sizeof(info)))
543 return -EFAULT;
544
545 /* store copy of mask, because we zero struct later on */
546 sset_mask = info.sset_mask;
547 if (!sset_mask)
548 return 0;
549
550 /* calculate size of return buffer */
Jeff Garzikd17792e2010-03-04 08:21:53 +0000551 n_bits = hweight64(sset_mask);
Jeff Garzik723b2f52010-03-03 22:51:50 +0000552
553 memset(&info, 0, sizeof(info));
554 info.cmd = ETHTOOL_GSSET_INFO;
555
556 info_buf = kzalloc(n_bits * sizeof(u32), GFP_USER);
557 if (!info_buf)
558 return -ENOMEM;
559
560 /*
561 * fill return buffer based on input bitmask and successful
562 * get_sset_count return
563 */
564 for (i = 0; i < 64; i++) {
565 if (!(sset_mask & (1ULL << i)))
566 continue;
567
Michał Mirosław340ae162011-02-15 16:59:16 +0000568 rc = __ethtool_get_sset_count(dev, i);
Jeff Garzik723b2f52010-03-03 22:51:50 +0000569 if (rc >= 0) {
570 info.sset_mask |= (1ULL << i);
571 info_buf[idx++] = rc;
572 }
573 }
574
575 ret = -EFAULT;
576 if (copy_to_user(useraddr, &info, sizeof(info)))
577 goto out;
578
579 useraddr += offsetof(struct ethtool_sset_info, data);
580 if (copy_to_user(useraddr, info_buf, idx * sizeof(u32)))
581 goto out;
582
583 ret = 0;
584
585out:
586 kfree(info_buf);
587 return ret;
588}
589
chavey97f8aef2010-04-07 21:54:42 -0700590static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
Ben Hutchingsbf988432010-06-28 08:45:58 +0000591 u32 cmd, void __user *useraddr)
Santwona Behera0853ad62008-07-02 03:47:41 -0700592{
Ben Hutchingsbf988432010-06-28 08:45:58 +0000593 struct ethtool_rxnfc info;
594 size_t info_size = sizeof(info);
Santwona Behera0853ad62008-07-02 03:47:41 -0700595
Santwona Behera59089d82009-02-20 00:58:13 -0800596 if (!dev->ethtool_ops->set_rxnfc)
Santwona Behera0853ad62008-07-02 03:47:41 -0700597 return -EOPNOTSUPP;
598
Ben Hutchingsbf988432010-06-28 08:45:58 +0000599 /* struct ethtool_rxnfc was originally defined for
600 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
601 * members. User-space might still be using that
602 * definition. */
603 if (cmd == ETHTOOL_SRXFH)
604 info_size = (offsetof(struct ethtool_rxnfc, data) +
605 sizeof(info.data));
606
607 if (copy_from_user(&info, useraddr, info_size))
Santwona Behera0853ad62008-07-02 03:47:41 -0700608 return -EFAULT;
609
Ben Hutchingsbf988432010-06-28 08:45:58 +0000610 return dev->ethtool_ops->set_rxnfc(dev, &info);
Santwona Behera0853ad62008-07-02 03:47:41 -0700611}
612
chavey97f8aef2010-04-07 21:54:42 -0700613static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
Ben Hutchingsbf988432010-06-28 08:45:58 +0000614 u32 cmd, void __user *useraddr)
Santwona Behera0853ad62008-07-02 03:47:41 -0700615{
616 struct ethtool_rxnfc info;
Ben Hutchingsbf988432010-06-28 08:45:58 +0000617 size_t info_size = sizeof(info);
Santwona Behera59089d82009-02-20 00:58:13 -0800618 const struct ethtool_ops *ops = dev->ethtool_ops;
619 int ret;
620 void *rule_buf = NULL;
Santwona Behera0853ad62008-07-02 03:47:41 -0700621
Santwona Behera59089d82009-02-20 00:58:13 -0800622 if (!ops->get_rxnfc)
Santwona Behera0853ad62008-07-02 03:47:41 -0700623 return -EOPNOTSUPP;
624
Ben Hutchingsbf988432010-06-28 08:45:58 +0000625 /* struct ethtool_rxnfc was originally defined for
626 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
627 * members. User-space might still be using that
628 * definition. */
629 if (cmd == ETHTOOL_GRXFH)
630 info_size = (offsetof(struct ethtool_rxnfc, data) +
631 sizeof(info.data));
632
633 if (copy_from_user(&info, useraddr, info_size))
Santwona Behera0853ad62008-07-02 03:47:41 -0700634 return -EFAULT;
635
Santwona Behera59089d82009-02-20 00:58:13 -0800636 if (info.cmd == ETHTOOL_GRXCLSRLALL) {
637 if (info.rule_cnt > 0) {
Ben Hutchingsdb048b62010-06-28 08:44:07 +0000638 if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
Kees Cookae6df5f2010-10-07 10:03:48 +0000639 rule_buf = kzalloc(info.rule_cnt * sizeof(u32),
Ben Hutchingsdb048b62010-06-28 08:44:07 +0000640 GFP_USER);
Santwona Behera59089d82009-02-20 00:58:13 -0800641 if (!rule_buf)
642 return -ENOMEM;
643 }
644 }
Santwona Behera0853ad62008-07-02 03:47:41 -0700645
Santwona Behera59089d82009-02-20 00:58:13 -0800646 ret = ops->get_rxnfc(dev, &info, rule_buf);
647 if (ret < 0)
648 goto err_out;
649
650 ret = -EFAULT;
Ben Hutchingsbf988432010-06-28 08:45:58 +0000651 if (copy_to_user(useraddr, &info, info_size))
Santwona Behera59089d82009-02-20 00:58:13 -0800652 goto err_out;
653
654 if (rule_buf) {
655 useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
656 if (copy_to_user(useraddr, rule_buf,
657 info.rule_cnt * sizeof(u32)))
658 goto err_out;
659 }
660 ret = 0;
661
662err_out:
Wei Yongjunc9cacec2009-03-31 15:06:26 -0700663 kfree(rule_buf);
Santwona Behera59089d82009-02-20 00:58:13 -0800664
665 return ret;
Santwona Behera0853ad62008-07-02 03:47:41 -0700666}
667
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000668static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
669 void __user *useraddr)
670{
671 struct ethtool_rxfh_indir *indir;
672 u32 table_size;
673 size_t full_size;
674 int ret;
675
676 if (!dev->ethtool_ops->get_rxfh_indir)
677 return -EOPNOTSUPP;
678
679 if (copy_from_user(&table_size,
680 useraddr + offsetof(struct ethtool_rxfh_indir, size),
681 sizeof(table_size)))
682 return -EFAULT;
683
684 if (table_size >
685 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
686 return -ENOMEM;
687 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
Kees Cookb00916b2010-10-11 12:23:25 -0700688 indir = kzalloc(full_size, GFP_USER);
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000689 if (!indir)
690 return -ENOMEM;
691
692 indir->cmd = ETHTOOL_GRXFHINDIR;
693 indir->size = table_size;
694 ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
695 if (ret)
696 goto out;
697
698 if (copy_to_user(useraddr, indir, full_size))
699 ret = -EFAULT;
700
701out:
702 kfree(indir);
703 return ret;
704}
705
706static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
707 void __user *useraddr)
708{
709 struct ethtool_rxfh_indir *indir;
710 u32 table_size;
711 size_t full_size;
712 int ret;
713
714 if (!dev->ethtool_ops->set_rxfh_indir)
715 return -EOPNOTSUPP;
716
717 if (copy_from_user(&table_size,
718 useraddr + offsetof(struct ethtool_rxfh_indir, size),
719 sizeof(table_size)))
720 return -EFAULT;
721
722 if (table_size >
723 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
724 return -ENOMEM;
725 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
726 indir = kmalloc(full_size, GFP_USER);
727 if (!indir)
728 return -ENOMEM;
729
730 if (copy_from_user(indir, useraddr, full_size)) {
731 ret = -EFAULT;
732 goto out;
733 }
734
735 ret = dev->ethtool_ops->set_rxfh_indir(dev, indir);
736
737out:
738 kfree(indir);
739 return ret;
740}
741
Peter Waskiewicze8589112010-02-12 13:48:05 +0000742static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
chavey97f8aef2010-04-07 21:54:42 -0700743 struct ethtool_rx_ntuple_flow_spec *spec,
744 struct ethtool_rx_ntuple_flow_spec_container *fsc)
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800745{
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800746
747 /* don't add filters forever */
Peter Waskiewicze8589112010-02-12 13:48:05 +0000748 if (list->count >= ETHTOOL_MAX_NTUPLE_LIST_ENTRY) {
749 /* free the container */
750 kfree(fsc);
751 return;
752 }
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800753
754 /* Copy the whole filter over */
755 fsc->fs.flow_type = spec->flow_type;
756 memcpy(&fsc->fs.h_u, &spec->h_u, sizeof(spec->h_u));
757 memcpy(&fsc->fs.m_u, &spec->m_u, sizeof(spec->m_u));
758
759 fsc->fs.vlan_tag = spec->vlan_tag;
760 fsc->fs.vlan_tag_mask = spec->vlan_tag_mask;
761 fsc->fs.data = spec->data;
762 fsc->fs.data_mask = spec->data_mask;
763 fsc->fs.action = spec->action;
764
765 /* add to the list */
766 list_add_tail_rcu(&fsc->list, &list->list);
767 list->count++;
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800768}
769
Ben Hutchingsbe2902d2010-09-16 11:28:07 +0000770/*
771 * ethtool does not (or did not) set masks for flow parameters that are
772 * not specified, so if both value and mask are 0 then this must be
773 * treated as equivalent to a mask with all bits set. Implement that
774 * here rather than in drivers.
775 */
776static void rx_ntuple_fix_masks(struct ethtool_rx_ntuple_flow_spec *fs)
777{
778 struct ethtool_tcpip4_spec *entry = &fs->h_u.tcp_ip4_spec;
779 struct ethtool_tcpip4_spec *mask = &fs->m_u.tcp_ip4_spec;
780
781 if (fs->flow_type != TCP_V4_FLOW &&
782 fs->flow_type != UDP_V4_FLOW &&
783 fs->flow_type != SCTP_V4_FLOW)
784 return;
785
786 if (!(entry->ip4src | mask->ip4src))
787 mask->ip4src = htonl(0xffffffff);
788 if (!(entry->ip4dst | mask->ip4dst))
789 mask->ip4dst = htonl(0xffffffff);
790 if (!(entry->psrc | mask->psrc))
791 mask->psrc = htons(0xffff);
792 if (!(entry->pdst | mask->pdst))
793 mask->pdst = htons(0xffff);
794 if (!(entry->tos | mask->tos))
795 mask->tos = 0xff;
796 if (!(fs->vlan_tag | fs->vlan_tag_mask))
797 fs->vlan_tag_mask = 0xffff;
798 if (!(fs->data | fs->data_mask))
799 fs->data_mask = 0xffffffffffffffffULL;
800}
801
chavey97f8aef2010-04-07 21:54:42 -0700802static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev,
803 void __user *useraddr)
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800804{
805 struct ethtool_rx_ntuple cmd;
806 const struct ethtool_ops *ops = dev->ethtool_ops;
Peter Waskiewicze8589112010-02-12 13:48:05 +0000807 struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL;
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800808 int ret;
809
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800810 if (!(dev->features & NETIF_F_NTUPLE))
811 return -EINVAL;
812
813 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
814 return -EFAULT;
815
Ben Hutchingsbe2902d2010-09-16 11:28:07 +0000816 rx_ntuple_fix_masks(&cmd.fs);
817
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800818 /*
819 * Cache filter in dev struct for GET operation only if
820 * the underlying driver doesn't have its own GET operation, and
Peter Waskiewicze8589112010-02-12 13:48:05 +0000821 * only if the filter was added successfully. First make sure we
822 * can allocate the filter, then continue if successful.
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800823 */
Peter Waskiewicze8589112010-02-12 13:48:05 +0000824 if (!ops->get_rx_ntuple) {
825 fsc = kmalloc(sizeof(*fsc), GFP_ATOMIC);
826 if (!fsc)
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800827 return -ENOMEM;
Peter Waskiewicze8589112010-02-12 13:48:05 +0000828 }
829
830 ret = ops->set_rx_ntuple(dev, &cmd);
831 if (ret) {
832 kfree(fsc);
833 return ret;
834 }
835
836 if (!ops->get_rx_ntuple)
837 __rx_ntuple_filter_add(&dev->ethtool_ntuple_list, &cmd.fs, fsc);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800838
839 return ret;
840}
841
842static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr)
843{
844 struct ethtool_gstrings gstrings;
845 const struct ethtool_ops *ops = dev->ethtool_ops;
846 struct ethtool_rx_ntuple_flow_spec_container *fsc;
847 u8 *data;
848 char *p;
849 int ret, i, num_strings = 0;
850
851 if (!ops->get_sset_count)
852 return -EOPNOTSUPP;
853
854 if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
855 return -EFAULT;
856
857 ret = ops->get_sset_count(dev, gstrings.string_set);
858 if (ret < 0)
859 return ret;
860
861 gstrings.len = ret;
862
Kees Cookb00916b2010-10-11 12:23:25 -0700863 data = kzalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800864 if (!data)
865 return -ENOMEM;
866
867 if (ops->get_rx_ntuple) {
868 /* driver-specific filter grab */
869 ret = ops->get_rx_ntuple(dev, gstrings.string_set, data);
870 goto copy;
871 }
872
873 /* default ethtool filter grab */
874 i = 0;
875 p = (char *)data;
876 list_for_each_entry(fsc, &dev->ethtool_ntuple_list.list, list) {
877 sprintf(p, "Filter %d:\n", i);
878 p += ETH_GSTRING_LEN;
879 num_strings++;
880
881 switch (fsc->fs.flow_type) {
882 case TCP_V4_FLOW:
883 sprintf(p, "\tFlow Type: TCP\n");
884 p += ETH_GSTRING_LEN;
885 num_strings++;
886 break;
887 case UDP_V4_FLOW:
888 sprintf(p, "\tFlow Type: UDP\n");
889 p += ETH_GSTRING_LEN;
890 num_strings++;
891 break;
892 case SCTP_V4_FLOW:
893 sprintf(p, "\tFlow Type: SCTP\n");
894 p += ETH_GSTRING_LEN;
895 num_strings++;
896 break;
897 case AH_ESP_V4_FLOW:
898 sprintf(p, "\tFlow Type: AH ESP\n");
899 p += ETH_GSTRING_LEN;
900 num_strings++;
901 break;
902 case ESP_V4_FLOW:
903 sprintf(p, "\tFlow Type: ESP\n");
904 p += ETH_GSTRING_LEN;
905 num_strings++;
906 break;
907 case IP_USER_FLOW:
908 sprintf(p, "\tFlow Type: Raw IP\n");
909 p += ETH_GSTRING_LEN;
910 num_strings++;
911 break;
912 case IPV4_FLOW:
913 sprintf(p, "\tFlow Type: IPv4\n");
914 p += ETH_GSTRING_LEN;
915 num_strings++;
916 break;
917 default:
918 sprintf(p, "\tFlow Type: Unknown\n");
919 p += ETH_GSTRING_LEN;
920 num_strings++;
921 goto unknown_filter;
Joe Perchesccbd6a52010-05-14 10:58:26 +0000922 }
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800923
924 /* now the rest of the filters */
925 switch (fsc->fs.flow_type) {
926 case TCP_V4_FLOW:
927 case UDP_V4_FLOW:
928 case SCTP_V4_FLOW:
929 sprintf(p, "\tSrc IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700930 fsc->fs.h_u.tcp_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800931 p += ETH_GSTRING_LEN;
932 num_strings++;
933 sprintf(p, "\tSrc IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700934 fsc->fs.m_u.tcp_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800935 p += ETH_GSTRING_LEN;
936 num_strings++;
937 sprintf(p, "\tDest IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700938 fsc->fs.h_u.tcp_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800939 p += ETH_GSTRING_LEN;
940 num_strings++;
941 sprintf(p, "\tDest IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700942 fsc->fs.m_u.tcp_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800943 p += ETH_GSTRING_LEN;
944 num_strings++;
945 sprintf(p, "\tSrc Port: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700946 fsc->fs.h_u.tcp_ip4_spec.psrc,
947 fsc->fs.m_u.tcp_ip4_spec.psrc);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800948 p += ETH_GSTRING_LEN;
949 num_strings++;
950 sprintf(p, "\tDest Port: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700951 fsc->fs.h_u.tcp_ip4_spec.pdst,
952 fsc->fs.m_u.tcp_ip4_spec.pdst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800953 p += ETH_GSTRING_LEN;
954 num_strings++;
955 sprintf(p, "\tTOS: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700956 fsc->fs.h_u.tcp_ip4_spec.tos,
957 fsc->fs.m_u.tcp_ip4_spec.tos);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800958 p += ETH_GSTRING_LEN;
959 num_strings++;
960 break;
961 case AH_ESP_V4_FLOW:
962 case ESP_V4_FLOW:
963 sprintf(p, "\tSrc IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700964 fsc->fs.h_u.ah_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800965 p += ETH_GSTRING_LEN;
966 num_strings++;
967 sprintf(p, "\tSrc IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700968 fsc->fs.m_u.ah_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800969 p += ETH_GSTRING_LEN;
970 num_strings++;
971 sprintf(p, "\tDest IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700972 fsc->fs.h_u.ah_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800973 p += ETH_GSTRING_LEN;
974 num_strings++;
975 sprintf(p, "\tDest IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700976 fsc->fs.m_u.ah_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800977 p += ETH_GSTRING_LEN;
978 num_strings++;
979 sprintf(p, "\tSPI: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700980 fsc->fs.h_u.ah_ip4_spec.spi,
981 fsc->fs.m_u.ah_ip4_spec.spi);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800982 p += ETH_GSTRING_LEN;
983 num_strings++;
984 sprintf(p, "\tTOS: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700985 fsc->fs.h_u.ah_ip4_spec.tos,
986 fsc->fs.m_u.ah_ip4_spec.tos);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800987 p += ETH_GSTRING_LEN;
988 num_strings++;
989 break;
990 case IP_USER_FLOW:
991 sprintf(p, "\tSrc IP addr: 0x%x\n",
Ben Hutchingse0de7c92010-09-14 09:13:08 +0000992 fsc->fs.h_u.usr_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800993 p += ETH_GSTRING_LEN;
994 num_strings++;
995 sprintf(p, "\tSrc IP mask: 0x%x\n",
Ben Hutchingse0de7c92010-09-14 09:13:08 +0000996 fsc->fs.m_u.usr_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800997 p += ETH_GSTRING_LEN;
998 num_strings++;
999 sprintf(p, "\tDest IP addr: 0x%x\n",
Ben Hutchingse0de7c92010-09-14 09:13:08 +00001000 fsc->fs.h_u.usr_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001001 p += ETH_GSTRING_LEN;
1002 num_strings++;
1003 sprintf(p, "\tDest IP mask: 0x%x\n",
Ben Hutchingse0de7c92010-09-14 09:13:08 +00001004 fsc->fs.m_u.usr_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001005 p += ETH_GSTRING_LEN;
1006 num_strings++;
1007 break;
1008 case IPV4_FLOW:
1009 sprintf(p, "\tSrc IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001010 fsc->fs.h_u.usr_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001011 p += ETH_GSTRING_LEN;
1012 num_strings++;
1013 sprintf(p, "\tSrc IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001014 fsc->fs.m_u.usr_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001015 p += ETH_GSTRING_LEN;
1016 num_strings++;
1017 sprintf(p, "\tDest IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001018 fsc->fs.h_u.usr_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001019 p += ETH_GSTRING_LEN;
1020 num_strings++;
1021 sprintf(p, "\tDest IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001022 fsc->fs.m_u.usr_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001023 p += ETH_GSTRING_LEN;
1024 num_strings++;
1025 sprintf(p, "\tL4 bytes: 0x%x, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001026 fsc->fs.h_u.usr_ip4_spec.l4_4_bytes,
1027 fsc->fs.m_u.usr_ip4_spec.l4_4_bytes);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001028 p += ETH_GSTRING_LEN;
1029 num_strings++;
1030 sprintf(p, "\tTOS: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001031 fsc->fs.h_u.usr_ip4_spec.tos,
1032 fsc->fs.m_u.usr_ip4_spec.tos);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001033 p += ETH_GSTRING_LEN;
1034 num_strings++;
1035 sprintf(p, "\tIP Version: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001036 fsc->fs.h_u.usr_ip4_spec.ip_ver,
1037 fsc->fs.m_u.usr_ip4_spec.ip_ver);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001038 p += ETH_GSTRING_LEN;
1039 num_strings++;
1040 sprintf(p, "\tProtocol: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001041 fsc->fs.h_u.usr_ip4_spec.proto,
1042 fsc->fs.m_u.usr_ip4_spec.proto);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001043 p += ETH_GSTRING_LEN;
1044 num_strings++;
1045 break;
Joe Perchesccbd6a52010-05-14 10:58:26 +00001046 }
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001047 sprintf(p, "\tVLAN: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -07001048 fsc->fs.vlan_tag, fsc->fs.vlan_tag_mask);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001049 p += ETH_GSTRING_LEN;
1050 num_strings++;
1051 sprintf(p, "\tUser-defined: 0x%Lx\n", fsc->fs.data);
1052 p += ETH_GSTRING_LEN;
1053 num_strings++;
1054 sprintf(p, "\tUser-defined mask: 0x%Lx\n", fsc->fs.data_mask);
1055 p += ETH_GSTRING_LEN;
1056 num_strings++;
1057 if (fsc->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP)
1058 sprintf(p, "\tAction: Drop\n");
1059 else
1060 sprintf(p, "\tAction: Direct to queue %d\n",
chavey97f8aef2010-04-07 21:54:42 -07001061 fsc->fs.action);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001062 p += ETH_GSTRING_LEN;
1063 num_strings++;
1064unknown_filter:
1065 i++;
1066 }
1067copy:
1068 /* indicate to userspace how many strings we actually have */
1069 gstrings.len = num_strings;
1070 ret = -EFAULT;
1071 if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
1072 goto out;
1073 useraddr += sizeof(gstrings);
1074 if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
1075 goto out;
1076 ret = 0;
1077
1078out:
1079 kfree(data);
1080 return ret;
1081}
1082
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
1084{
1085 struct ethtool_regs regs;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001086 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 void *regbuf;
1088 int reglen, ret;
1089
1090 if (!ops->get_regs || !ops->get_regs_len)
1091 return -EOPNOTSUPP;
1092
1093 if (copy_from_user(&regs, useraddr, sizeof(regs)))
1094 return -EFAULT;
1095
1096 reglen = ops->get_regs_len(dev);
1097 if (regs.len > reglen)
1098 regs.len = reglen;
1099
Eugene Teob7c7d012011-01-24 21:05:17 -08001100 regbuf = vzalloc(reglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 if (!regbuf)
1102 return -ENOMEM;
1103
1104 ops->get_regs(dev, &regs, regbuf);
1105
1106 ret = -EFAULT;
1107 if (copy_to_user(useraddr, &regs, sizeof(regs)))
1108 goto out;
1109 useraddr += offsetof(struct ethtool_regs, data);
1110 if (copy_to_user(useraddr, regbuf, regs.len))
1111 goto out;
1112 ret = 0;
1113
1114 out:
Ben Hutchingsa77f5db2010-09-20 08:42:17 +00001115 vfree(regbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 return ret;
1117}
1118
Ben Hutchingsd73d3a82009-10-05 10:59:58 +00001119static int ethtool_reset(struct net_device *dev, char __user *useraddr)
1120{
1121 struct ethtool_value reset;
1122 int ret;
1123
1124 if (!dev->ethtool_ops->reset)
1125 return -EOPNOTSUPP;
1126
1127 if (copy_from_user(&reset, useraddr, sizeof(reset)))
1128 return -EFAULT;
1129
1130 ret = dev->ethtool_ops->reset(dev, &reset.data);
1131 if (ret)
1132 return ret;
1133
1134 if (copy_to_user(useraddr, &reset, sizeof(reset)))
1135 return -EFAULT;
1136 return 0;
1137}
1138
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
1140{
Roland Dreier8e557422010-02-11 12:14:23 -08001141 struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142
1143 if (!dev->ethtool_ops->get_wol)
1144 return -EOPNOTSUPP;
1145
1146 dev->ethtool_ops->get_wol(dev, &wol);
1147
1148 if (copy_to_user(useraddr, &wol, sizeof(wol)))
1149 return -EFAULT;
1150 return 0;
1151}
1152
1153static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
1154{
1155 struct ethtool_wolinfo wol;
1156
1157 if (!dev->ethtool_ops->set_wol)
1158 return -EOPNOTSUPP;
1159
1160 if (copy_from_user(&wol, useraddr, sizeof(wol)))
1161 return -EFAULT;
1162
1163 return dev->ethtool_ops->set_wol(dev, &wol);
1164}
1165
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166static int ethtool_nway_reset(struct net_device *dev)
1167{
1168 if (!dev->ethtool_ops->nway_reset)
1169 return -EOPNOTSUPP;
1170
1171 return dev->ethtool_ops->nway_reset(dev);
1172}
1173
Ben Hutchingse596e6e2010-12-09 12:08:35 +00001174static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
1175{
1176 struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
1177
1178 if (!dev->ethtool_ops->get_link)
1179 return -EOPNOTSUPP;
1180
1181 edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev);
1182
1183 if (copy_to_user(useraddr, &edata, sizeof(edata)))
1184 return -EFAULT;
1185 return 0;
1186}
1187
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
1189{
1190 struct ethtool_eeprom eeprom;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001191 const struct ethtool_ops *ops = dev->ethtool_ops;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001192 void __user *userbuf = useraddr + sizeof(eeprom);
1193 u32 bytes_remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 u8 *data;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001195 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196
1197 if (!ops->get_eeprom || !ops->get_eeprom_len)
1198 return -EOPNOTSUPP;
1199
1200 if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
1201 return -EFAULT;
1202
1203 /* Check for wrap and zero */
1204 if (eeprom.offset + eeprom.len <= eeprom.offset)
1205 return -EINVAL;
1206
1207 /* Check for exceeding total eeprom len */
1208 if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
1209 return -EINVAL;
1210
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001211 data = kmalloc(PAGE_SIZE, GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 if (!data)
1213 return -ENOMEM;
1214
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001215 bytes_remaining = eeprom.len;
1216 while (bytes_remaining > 0) {
1217 eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001219 ret = ops->get_eeprom(dev, &eeprom, data);
1220 if (ret)
1221 break;
1222 if (copy_to_user(userbuf, data, eeprom.len)) {
1223 ret = -EFAULT;
1224 break;
1225 }
1226 userbuf += eeprom.len;
1227 eeprom.offset += eeprom.len;
1228 bytes_remaining -= eeprom.len;
1229 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230
Mandeep Singh Bainesc5835df2008-04-24 20:55:56 -07001231 eeprom.len = userbuf - (useraddr + sizeof(eeprom));
1232 eeprom.offset -= eeprom.len;
1233 if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
1234 ret = -EFAULT;
1235
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 kfree(data);
1237 return ret;
1238}
1239
1240static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
1241{
1242 struct ethtool_eeprom eeprom;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001243 const struct ethtool_ops *ops = dev->ethtool_ops;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001244 void __user *userbuf = useraddr + sizeof(eeprom);
1245 u32 bytes_remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 u8 *data;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001247 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248
1249 if (!ops->set_eeprom || !ops->get_eeprom_len)
1250 return -EOPNOTSUPP;
1251
1252 if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
1253 return -EFAULT;
1254
1255 /* Check for wrap and zero */
1256 if (eeprom.offset + eeprom.len <= eeprom.offset)
1257 return -EINVAL;
1258
1259 /* Check for exceeding total eeprom len */
1260 if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
1261 return -EINVAL;
1262
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001263 data = kmalloc(PAGE_SIZE, GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 if (!data)
1265 return -ENOMEM;
1266
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001267 bytes_remaining = eeprom.len;
1268 while (bytes_remaining > 0) {
1269 eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001271 if (copy_from_user(data, userbuf, eeprom.len)) {
1272 ret = -EFAULT;
1273 break;
1274 }
1275 ret = ops->set_eeprom(dev, &eeprom, data);
1276 if (ret)
1277 break;
1278 userbuf += eeprom.len;
1279 eeprom.offset += eeprom.len;
1280 bytes_remaining -= eeprom.len;
1281 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 kfree(data);
1284 return ret;
1285}
1286
chavey97f8aef2010-04-07 21:54:42 -07001287static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev,
1288 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289{
Roland Dreier8e557422010-02-11 12:14:23 -08001290 struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
1292 if (!dev->ethtool_ops->get_coalesce)
1293 return -EOPNOTSUPP;
1294
1295 dev->ethtool_ops->get_coalesce(dev, &coalesce);
1296
1297 if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
1298 return -EFAULT;
1299 return 0;
1300}
1301
chavey97f8aef2010-04-07 21:54:42 -07001302static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
1303 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304{
1305 struct ethtool_coalesce coalesce;
1306
David S. Millerfa04ae52005-06-06 15:07:19 -07001307 if (!dev->ethtool_ops->set_coalesce)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 return -EOPNOTSUPP;
1309
1310 if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
1311 return -EFAULT;
1312
1313 return dev->ethtool_ops->set_coalesce(dev, &coalesce);
1314}
1315
1316static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
1317{
Roland Dreier8e557422010-02-11 12:14:23 -08001318 struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319
1320 if (!dev->ethtool_ops->get_ringparam)
1321 return -EOPNOTSUPP;
1322
1323 dev->ethtool_ops->get_ringparam(dev, &ringparam);
1324
1325 if (copy_to_user(useraddr, &ringparam, sizeof(ringparam)))
1326 return -EFAULT;
1327 return 0;
1328}
1329
1330static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
1331{
1332 struct ethtool_ringparam ringparam;
1333
1334 if (!dev->ethtool_ops->set_ringparam)
1335 return -EOPNOTSUPP;
1336
1337 if (copy_from_user(&ringparam, useraddr, sizeof(ringparam)))
1338 return -EFAULT;
1339
1340 return dev->ethtool_ops->set_ringparam(dev, &ringparam);
1341}
1342
1343static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
1344{
1345 struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };
1346
1347 if (!dev->ethtool_ops->get_pauseparam)
1348 return -EOPNOTSUPP;
1349
1350 dev->ethtool_ops->get_pauseparam(dev, &pauseparam);
1351
1352 if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam)))
1353 return -EFAULT;
1354 return 0;
1355}
1356
1357static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
1358{
1359 struct ethtool_pauseparam pauseparam;
1360
Jeff Garzike1b90c42006-07-17 12:54:40 -04001361 if (!dev->ethtool_ops->set_pauseparam)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 return -EOPNOTSUPP;
1363
1364 if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))
1365 return -EFAULT;
1366
1367 return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
1368}
1369
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370static int __ethtool_set_sg(struct net_device *dev, u32 data)
1371{
1372 int err;
1373
Michał Mirosław0a417702011-02-15 16:59:17 +00001374 if (data && !(dev->features & NETIF_F_ALL_CSUM))
1375 return -EINVAL;
1376
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 if (!data && dev->ethtool_ops->set_tso) {
1378 err = dev->ethtool_ops->set_tso(dev, 0);
1379 if (err)
1380 return err;
1381 }
1382
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001383 if (!data && dev->ethtool_ops->set_ufo) {
1384 err = dev->ethtool_ops->set_ufo(dev, 0);
1385 if (err)
1386 return err;
1387 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 return dev->ethtool_ops->set_sg(dev, data);
1389}
1390
Michał Mirosław0a417702011-02-15 16:59:17 +00001391static int __ethtool_set_tx_csum(struct net_device *dev, u32 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 int err;
1394
1395 if (!dev->ethtool_ops->set_tx_csum)
1396 return -EOPNOTSUPP;
1397
Michał Mirosław0a417702011-02-15 16:59:17 +00001398 if (!data && dev->ethtool_ops->set_sg) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 err = __ethtool_set_sg(dev, 0);
1400 if (err)
1401 return err;
1402 }
1403
Michał Mirosław0a417702011-02-15 16:59:17 +00001404 return dev->ethtool_ops->set_tx_csum(dev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405}
1406
Herbert Xub240a0e2008-12-15 23:44:31 -08001407static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
1408{
1409 struct ethtool_value edata;
1410
1411 if (!dev->ethtool_ops->set_rx_csum)
1412 return -EOPNOTSUPP;
1413
1414 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1415 return -EFAULT;
1416
1417 if (!edata.data && dev->ethtool_ops->set_sg)
1418 dev->features &= ~NETIF_F_GRO;
1419
1420 return dev->ethtool_ops->set_rx_csum(dev, edata.data);
1421}
1422
Michał Mirosław0a417702011-02-15 16:59:17 +00001423static int __ethtool_set_tso(struct net_device *dev, u32 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 if (!dev->ethtool_ops->set_tso)
1426 return -EOPNOTSUPP;
1427
Michał Mirosław0a417702011-02-15 16:59:17 +00001428 if (data && !(dev->features & NETIF_F_SG))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 return -EINVAL;
1430
Michał Mirosław0a417702011-02-15 16:59:17 +00001431 return dev->ethtool_ops->set_tso(dev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432}
1433
Michał Mirosław0a417702011-02-15 16:59:17 +00001434static int __ethtool_set_ufo(struct net_device *dev, u32 data)
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001435{
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001436 if (!dev->ethtool_ops->set_ufo)
1437 return -EOPNOTSUPP;
Michał Mirosław0a417702011-02-15 16:59:17 +00001438 if (data && !(dev->features & NETIF_F_SG))
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001439 return -EINVAL;
Michał Mirosław0a417702011-02-15 16:59:17 +00001440 if (data && !((dev->features & NETIF_F_GEN_CSUM) ||
Michał Mirosław79032642010-11-30 06:38:00 +00001441 (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
1442 == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)))
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001443 return -EINVAL;
Michał Mirosław0a417702011-02-15 16:59:17 +00001444 return dev->ethtool_ops->set_ufo(dev, data);
Herbert Xub240a0e2008-12-15 23:44:31 -08001445}
1446
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
1448{
1449 struct ethtool_test test;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001450 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 u64 *data;
Jeff Garzikff03d492007-08-15 16:01:08 -07001452 int ret, test_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001454 if (!ops->self_test || !ops->get_sset_count)
Jeff Garzikff03d492007-08-15 16:01:08 -07001455 return -EOPNOTSUPP;
1456
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001457 test_len = ops->get_sset_count(dev, ETH_SS_TEST);
Jeff Garzikff03d492007-08-15 16:01:08 -07001458 if (test_len < 0)
1459 return test_len;
1460 WARN_ON(test_len == 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461
1462 if (copy_from_user(&test, useraddr, sizeof(test)))
1463 return -EFAULT;
1464
Jeff Garzikff03d492007-08-15 16:01:08 -07001465 test.len = test_len;
1466 data = kmalloc(test_len * sizeof(u64), GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 if (!data)
1468 return -ENOMEM;
1469
1470 ops->self_test(dev, &test, data);
1471
1472 ret = -EFAULT;
1473 if (copy_to_user(useraddr, &test, sizeof(test)))
1474 goto out;
1475 useraddr += sizeof(test);
1476 if (copy_to_user(useraddr, data, test.len * sizeof(u64)))
1477 goto out;
1478 ret = 0;
1479
1480 out:
1481 kfree(data);
1482 return ret;
1483}
1484
1485static int ethtool_get_strings(struct net_device *dev, void __user *useraddr)
1486{
1487 struct ethtool_gstrings gstrings;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 u8 *data;
1489 int ret;
1490
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
1492 return -EFAULT;
1493
Michał Mirosław340ae162011-02-15 16:59:16 +00001494 ret = __ethtool_get_sset_count(dev, gstrings.string_set);
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001495 if (ret < 0)
1496 return ret;
Jeff Garzikff03d492007-08-15 16:01:08 -07001497
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001498 gstrings.len = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499
1500 data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
1501 if (!data)
1502 return -ENOMEM;
1503
Michał Mirosław340ae162011-02-15 16:59:16 +00001504 __ethtool_get_strings(dev, gstrings.string_set, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 ret = -EFAULT;
1507 if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
1508 goto out;
1509 useraddr += sizeof(gstrings);
1510 if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
1511 goto out;
1512 ret = 0;
1513
Michał Mirosław340ae162011-02-15 16:59:16 +00001514out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 kfree(data);
1516 return ret;
1517}
1518
1519static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
1520{
1521 struct ethtool_value id;
1522
1523 if (!dev->ethtool_ops->phys_id)
1524 return -EOPNOTSUPP;
1525
1526 if (copy_from_user(&id, useraddr, sizeof(id)))
1527 return -EFAULT;
1528
1529 return dev->ethtool_ops->phys_id(dev, id.data);
1530}
1531
1532static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
1533{
1534 struct ethtool_stats stats;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001535 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 u64 *data;
Jeff Garzikff03d492007-08-15 16:01:08 -07001537 int ret, n_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001539 if (!ops->get_ethtool_stats || !ops->get_sset_count)
Jeff Garzikff03d492007-08-15 16:01:08 -07001540 return -EOPNOTSUPP;
1541
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001542 n_stats = ops->get_sset_count(dev, ETH_SS_STATS);
Jeff Garzikff03d492007-08-15 16:01:08 -07001543 if (n_stats < 0)
1544 return n_stats;
1545 WARN_ON(n_stats == 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
1547 if (copy_from_user(&stats, useraddr, sizeof(stats)))
1548 return -EFAULT;
1549
Jeff Garzikff03d492007-08-15 16:01:08 -07001550 stats.n_stats = n_stats;
1551 data = kmalloc(n_stats * sizeof(u64), GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 if (!data)
1553 return -ENOMEM;
1554
1555 ops->get_ethtool_stats(dev, &stats, data);
1556
1557 ret = -EFAULT;
1558 if (copy_to_user(useraddr, &stats, sizeof(stats)))
1559 goto out;
1560 useraddr += sizeof(stats);
1561 if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64)))
1562 goto out;
1563 ret = 0;
1564
1565 out:
1566 kfree(data);
1567 return ret;
1568}
1569
viro@ftp.linux.org.uk0bf0519d2005-09-05 03:26:18 +01001570static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
Jon Wetzela6f9a702005-08-20 17:15:54 -07001571{
1572 struct ethtool_perm_addr epaddr;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001573
Matthew Wilcox313674a2007-07-31 14:00:29 -07001574 if (copy_from_user(&epaddr, useraddr, sizeof(epaddr)))
Jon Wetzela6f9a702005-08-20 17:15:54 -07001575 return -EFAULT;
1576
Matthew Wilcox313674a2007-07-31 14:00:29 -07001577 if (epaddr.size < dev->addr_len)
1578 return -ETOOSMALL;
1579 epaddr.size = dev->addr_len;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001580
Jon Wetzela6f9a702005-08-20 17:15:54 -07001581 if (copy_to_user(useraddr, &epaddr, sizeof(epaddr)))
Matthew Wilcox313674a2007-07-31 14:00:29 -07001582 return -EFAULT;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001583 useraddr += sizeof(epaddr);
Matthew Wilcox313674a2007-07-31 14:00:29 -07001584 if (copy_to_user(useraddr, dev->perm_addr, epaddr.size))
1585 return -EFAULT;
1586 return 0;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001587}
1588
Jeff Garzik13c99b22007-08-15 16:01:56 -07001589static int ethtool_get_value(struct net_device *dev, char __user *useraddr,
1590 u32 cmd, u32 (*actor)(struct net_device *))
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001591{
Roland Dreier8e557422010-02-11 12:14:23 -08001592 struct ethtool_value edata = { .cmd = cmd };
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001593
Jeff Garzik13c99b22007-08-15 16:01:56 -07001594 if (!actor)
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001595 return -EOPNOTSUPP;
1596
Jeff Garzik13c99b22007-08-15 16:01:56 -07001597 edata.data = actor(dev);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001598
1599 if (copy_to_user(useraddr, &edata, sizeof(edata)))
1600 return -EFAULT;
1601 return 0;
1602}
1603
Jeff Garzik13c99b22007-08-15 16:01:56 -07001604static int ethtool_set_value_void(struct net_device *dev, char __user *useraddr,
1605 void (*actor)(struct net_device *, u32))
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001606{
1607 struct ethtool_value edata;
1608
Jeff Garzik13c99b22007-08-15 16:01:56 -07001609 if (!actor)
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001610 return -EOPNOTSUPP;
1611
1612 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1613 return -EFAULT;
1614
Jeff Garzik13c99b22007-08-15 16:01:56 -07001615 actor(dev, edata.data);
Jeff Garzik339bf022007-08-15 16:01:32 -07001616 return 0;
1617}
1618
Jeff Garzik13c99b22007-08-15 16:01:56 -07001619static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
1620 int (*actor)(struct net_device *, u32))
Jeff Garzik339bf022007-08-15 16:01:32 -07001621{
1622 struct ethtool_value edata;
1623
Jeff Garzik13c99b22007-08-15 16:01:56 -07001624 if (!actor)
Jeff Garzik339bf022007-08-15 16:01:32 -07001625 return -EOPNOTSUPP;
1626
1627 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1628 return -EFAULT;
1629
Jeff Garzik13c99b22007-08-15 16:01:56 -07001630 return actor(dev, edata.data);
Jeff Garzik339bf022007-08-15 16:01:32 -07001631}
1632
chavey97f8aef2010-04-07 21:54:42 -07001633static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
1634 char __user *useraddr)
Ajit Khaparde05c6a8d2009-09-02 17:02:55 +00001635{
1636 struct ethtool_flash efl;
1637
1638 if (copy_from_user(&efl, useraddr, sizeof(efl)))
1639 return -EFAULT;
1640
1641 if (!dev->ethtool_ops->flash_device)
1642 return -EOPNOTSUPP;
1643
1644 return dev->ethtool_ops->flash_device(dev, &efl);
1645}
1646
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647/* The main entry point in this file. Called from net/core/dev.c */
1648
Eric W. Biederman881d9662007-09-17 11:56:21 -07001649int dev_ethtool(struct net *net, struct ifreq *ifr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650{
Eric W. Biederman881d9662007-09-17 11:56:21 -07001651 struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 void __user *useraddr = ifr->ifr_data;
1653 u32 ethcmd;
1654 int rc;
Michał Mirosław04ed3e72011-01-24 15:32:47 -08001655 u32 old_features;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 if (!dev || !netif_device_present(dev))
1658 return -ENODEV;
1659
chavey97f8aef2010-04-07 21:54:42 -07001660 if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 return -EFAULT;
1662
Ben Hutchings01414802010-08-17 02:31:15 -07001663 if (!dev->ethtool_ops) {
1664 /* ETHTOOL_GDRVINFO does not require any driver support.
1665 * It is also unprivileged and does not change anything,
1666 * so we can take a shortcut to it. */
1667 if (ethcmd == ETHTOOL_GDRVINFO)
1668 return ethtool_get_drvinfo(dev, useraddr);
1669 else
1670 return -EOPNOTSUPP;
1671 }
1672
Stephen Hemminger75f31232006-09-28 15:13:37 -07001673 /* Allow some commands to be done by anyone */
chavey97f8aef2010-04-07 21:54:42 -07001674 switch (ethcmd) {
stephen hemminger0fdc1002010-08-23 10:24:18 +00001675 case ETHTOOL_GSET:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001676 case ETHTOOL_GDRVINFO:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001677 case ETHTOOL_GMSGLVL:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001678 case ETHTOOL_GCOALESCE:
1679 case ETHTOOL_GRINGPARAM:
1680 case ETHTOOL_GPAUSEPARAM:
1681 case ETHTOOL_GRXCSUM:
1682 case ETHTOOL_GTXCSUM:
1683 case ETHTOOL_GSG:
1684 case ETHTOOL_GSTRINGS:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001685 case ETHTOOL_GTSO:
1686 case ETHTOOL_GPERMADDR:
1687 case ETHTOOL_GUFO:
1688 case ETHTOOL_GGSO:
stephen hemminger1cab8192010-02-11 13:48:29 +00001689 case ETHTOOL_GGRO:
Jeff Garzik339bf022007-08-15 16:01:32 -07001690 case ETHTOOL_GFLAGS:
1691 case ETHTOOL_GPFLAGS:
Santwona Behera0853ad62008-07-02 03:47:41 -07001692 case ETHTOOL_GRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08001693 case ETHTOOL_GRXRINGS:
1694 case ETHTOOL_GRXCLSRLCNT:
1695 case ETHTOOL_GRXCLSRULE:
1696 case ETHTOOL_GRXCLSRLALL:
Michał Mirosław5455c692011-02-15 16:59:17 +00001697 case ETHTOOL_GFEATURES:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001698 break;
1699 default:
1700 if (!capable(CAP_NET_ADMIN))
1701 return -EPERM;
1702 }
1703
chavey97f8aef2010-04-07 21:54:42 -07001704 if (dev->ethtool_ops->begin) {
1705 rc = dev->ethtool_ops->begin(dev);
1706 if (rc < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 return rc;
chavey97f8aef2010-04-07 21:54:42 -07001708 }
Stephen Hemmingerd8a33ac2005-05-29 14:13:47 -07001709 old_features = dev->features;
1710
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 switch (ethcmd) {
1712 case ETHTOOL_GSET:
1713 rc = ethtool_get_settings(dev, useraddr);
1714 break;
1715 case ETHTOOL_SSET:
1716 rc = ethtool_set_settings(dev, useraddr);
1717 break;
1718 case ETHTOOL_GDRVINFO:
1719 rc = ethtool_get_drvinfo(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 break;
1721 case ETHTOOL_GREGS:
1722 rc = ethtool_get_regs(dev, useraddr);
1723 break;
1724 case ETHTOOL_GWOL:
1725 rc = ethtool_get_wol(dev, useraddr);
1726 break;
1727 case ETHTOOL_SWOL:
1728 rc = ethtool_set_wol(dev, useraddr);
1729 break;
1730 case ETHTOOL_GMSGLVL:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001731 rc = ethtool_get_value(dev, useraddr, ethcmd,
1732 dev->ethtool_ops->get_msglevel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 break;
1734 case ETHTOOL_SMSGLVL:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001735 rc = ethtool_set_value_void(dev, useraddr,
1736 dev->ethtool_ops->set_msglevel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 break;
1738 case ETHTOOL_NWAY_RST:
1739 rc = ethtool_nway_reset(dev);
1740 break;
1741 case ETHTOOL_GLINK:
Ben Hutchingse596e6e2010-12-09 12:08:35 +00001742 rc = ethtool_get_link(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 break;
1744 case ETHTOOL_GEEPROM:
1745 rc = ethtool_get_eeprom(dev, useraddr);
1746 break;
1747 case ETHTOOL_SEEPROM:
1748 rc = ethtool_set_eeprom(dev, useraddr);
1749 break;
1750 case ETHTOOL_GCOALESCE:
1751 rc = ethtool_get_coalesce(dev, useraddr);
1752 break;
1753 case ETHTOOL_SCOALESCE:
1754 rc = ethtool_set_coalesce(dev, useraddr);
1755 break;
1756 case ETHTOOL_GRINGPARAM:
1757 rc = ethtool_get_ringparam(dev, useraddr);
1758 break;
1759 case ETHTOOL_SRINGPARAM:
1760 rc = ethtool_set_ringparam(dev, useraddr);
1761 break;
1762 case ETHTOOL_GPAUSEPARAM:
1763 rc = ethtool_get_pauseparam(dev, useraddr);
1764 break;
1765 case ETHTOOL_SPAUSEPARAM:
1766 rc = ethtool_set_pauseparam(dev, useraddr);
1767 break;
1768 case ETHTOOL_GRXCSUM:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001769 rc = ethtool_get_value(dev, useraddr, ethcmd,
Sridhar Samudrala1896e612009-07-22 13:38:22 +00001770 (dev->ethtool_ops->get_rx_csum ?
1771 dev->ethtool_ops->get_rx_csum :
1772 ethtool_op_get_rx_csum));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 break;
1774 case ETHTOOL_SRXCSUM:
Herbert Xub240a0e2008-12-15 23:44:31 -08001775 rc = ethtool_set_rx_csum(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777 case ETHTOOL_TEST:
1778 rc = ethtool_self_test(dev, useraddr);
1779 break;
1780 case ETHTOOL_GSTRINGS:
1781 rc = ethtool_get_strings(dev, useraddr);
1782 break;
1783 case ETHTOOL_PHYS_ID:
1784 rc = ethtool_phys_id(dev, useraddr);
1785 break;
1786 case ETHTOOL_GSTATS:
1787 rc = ethtool_get_stats(dev, useraddr);
1788 break;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001789 case ETHTOOL_GPERMADDR:
1790 rc = ethtool_get_perm_addr(dev, useraddr);
1791 break;
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001792 case ETHTOOL_GFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001793 rc = ethtool_get_value(dev, useraddr, ethcmd,
Sridhar Samudrala1896e612009-07-22 13:38:22 +00001794 (dev->ethtool_ops->get_flags ?
1795 dev->ethtool_ops->get_flags :
1796 ethtool_op_get_flags));
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001797 break;
1798 case ETHTOOL_SFLAGS:
Michał Mirosławda8ac86c2011-02-15 16:59:18 +00001799 rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001800 break;
Jeff Garzik339bf022007-08-15 16:01:32 -07001801 case ETHTOOL_GPFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001802 rc = ethtool_get_value(dev, useraddr, ethcmd,
1803 dev->ethtool_ops->get_priv_flags);
Jeff Garzik339bf022007-08-15 16:01:32 -07001804 break;
1805 case ETHTOOL_SPFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001806 rc = ethtool_set_value(dev, useraddr,
1807 dev->ethtool_ops->set_priv_flags);
Jeff Garzik339bf022007-08-15 16:01:32 -07001808 break;
Santwona Behera0853ad62008-07-02 03:47:41 -07001809 case ETHTOOL_GRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08001810 case ETHTOOL_GRXRINGS:
1811 case ETHTOOL_GRXCLSRLCNT:
1812 case ETHTOOL_GRXCLSRULE:
1813 case ETHTOOL_GRXCLSRLALL:
Ben Hutchingsbf988432010-06-28 08:45:58 +00001814 rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);
Santwona Behera0853ad62008-07-02 03:47:41 -07001815 break;
1816 case ETHTOOL_SRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08001817 case ETHTOOL_SRXCLSRLDEL:
1818 case ETHTOOL_SRXCLSRLINS:
Ben Hutchingsbf988432010-06-28 08:45:58 +00001819 rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
Santwona Behera0853ad62008-07-02 03:47:41 -07001820 break;
Ajit Khaparde05c6a8d2009-09-02 17:02:55 +00001821 case ETHTOOL_FLASHDEV:
1822 rc = ethtool_flash_device(dev, useraddr);
1823 break;
Ben Hutchingsd73d3a82009-10-05 10:59:58 +00001824 case ETHTOOL_RESET:
1825 rc = ethtool_reset(dev, useraddr);
1826 break;
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001827 case ETHTOOL_SRXNTUPLE:
1828 rc = ethtool_set_rx_ntuple(dev, useraddr);
1829 break;
1830 case ETHTOOL_GRXNTUPLE:
1831 rc = ethtool_get_rx_ntuple(dev, useraddr);
1832 break;
Jeff Garzik723b2f52010-03-03 22:51:50 +00001833 case ETHTOOL_GSSET_INFO:
1834 rc = ethtool_get_sset_info(dev, useraddr);
1835 break;
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +00001836 case ETHTOOL_GRXFHINDIR:
1837 rc = ethtool_get_rxfh_indir(dev, useraddr);
1838 break;
1839 case ETHTOOL_SRXFHINDIR:
1840 rc = ethtool_set_rxfh_indir(dev, useraddr);
1841 break;
Michał Mirosław5455c692011-02-15 16:59:17 +00001842 case ETHTOOL_GFEATURES:
1843 rc = ethtool_get_features(dev, useraddr);
1844 break;
1845 case ETHTOOL_SFEATURES:
1846 rc = ethtool_set_features(dev, useraddr);
1847 break;
Michał Mirosław0a417702011-02-15 16:59:17 +00001848 case ETHTOOL_GTXCSUM:
1849 case ETHTOOL_GSG:
1850 case ETHTOOL_GTSO:
1851 case ETHTOOL_GUFO:
1852 case ETHTOOL_GGSO:
1853 case ETHTOOL_GGRO:
1854 rc = ethtool_get_one_feature(dev, useraddr, ethcmd);
1855 break;
1856 case ETHTOOL_STXCSUM:
1857 case ETHTOOL_SSG:
1858 case ETHTOOL_STSO:
1859 case ETHTOOL_SUFO:
1860 case ETHTOOL_SGSO:
1861 case ETHTOOL_SGRO:
1862 rc = ethtool_set_one_feature(dev, useraddr, ethcmd);
1863 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864 default:
Matthew Wilcox61a44b92007-07-31 14:00:02 -07001865 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 }
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09001867
Stephen Hemmingere71a4782007-04-10 20:10:33 -07001868 if (dev->ethtool_ops->complete)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 dev->ethtool_ops->complete(dev);
Stephen Hemmingerd8a33ac2005-05-29 14:13:47 -07001870
1871 if (old_features != dev->features)
1872 netdev_features_change(dev);
1873
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875}