blob: 85aaeab862a88aab86c9997ff1de838f203dab3d [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ław340ae162011-02-15 16:59:16 +0000175static int __ethtool_get_sset_count(struct net_device *dev, int sset)
176{
177 const struct ethtool_ops *ops = dev->ethtool_ops;
178
179 if (ops && ops->get_sset_count && ops->get_strings)
180 return ops->get_sset_count(dev, sset);
181 else
182 return -EOPNOTSUPP;
183}
184
185static void __ethtool_get_strings(struct net_device *dev,
186 u32 stringset, u8 *data)
187{
188 const struct ethtool_ops *ops = dev->ethtool_ops;
189
190 /* ops->get_strings is valid because checked earlier */
191 ops->get_strings(dev, stringset, data);
192}
193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
195{
Roland Dreier8e557422010-02-11 12:14:23 -0800196 struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 int err;
198
199 if (!dev->ethtool_ops->get_settings)
200 return -EOPNOTSUPP;
201
202 err = dev->ethtool_ops->get_settings(dev, &cmd);
203 if (err < 0)
204 return err;
205
206 if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
207 return -EFAULT;
208 return 0;
209}
210
211static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
212{
213 struct ethtool_cmd cmd;
214
215 if (!dev->ethtool_ops->set_settings)
216 return -EOPNOTSUPP;
217
218 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
219 return -EFAULT;
220
221 return dev->ethtool_ops->set_settings(dev, &cmd);
222}
223
chavey97f8aef2010-04-07 21:54:42 -0700224static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
225 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226{
227 struct ethtool_drvinfo info;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700228 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 memset(&info, 0, sizeof(info));
231 info.cmd = ETHTOOL_GDRVINFO;
Ben Hutchings01414802010-08-17 02:31:15 -0700232 if (ops && ops->get_drvinfo) {
233 ops->get_drvinfo(dev, &info);
234 } else if (dev->dev.parent && dev->dev.parent->driver) {
235 strlcpy(info.bus_info, dev_name(dev->dev.parent),
236 sizeof(info.bus_info));
237 strlcpy(info.driver, dev->dev.parent->driver->name,
238 sizeof(info.driver));
239 } else {
240 return -EOPNOTSUPP;
241 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
Jeff Garzik723b2f52010-03-03 22:51:50 +0000243 /*
244 * this method of obtaining string set info is deprecated;
Jeff Garzikd17792e2010-03-04 08:21:53 +0000245 * Use ETHTOOL_GSSET_INFO instead.
Jeff Garzik723b2f52010-03-03 22:51:50 +0000246 */
Ben Hutchings01414802010-08-17 02:31:15 -0700247 if (ops && ops->get_sset_count) {
Jeff Garzikff03d492007-08-15 16:01:08 -0700248 int rc;
249
250 rc = ops->get_sset_count(dev, ETH_SS_TEST);
251 if (rc >= 0)
252 info.testinfo_len = rc;
253 rc = ops->get_sset_count(dev, ETH_SS_STATS);
254 if (rc >= 0)
255 info.n_stats = rc;
Jeff Garzik339bf022007-08-15 16:01:32 -0700256 rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
257 if (rc >= 0)
258 info.n_priv_flags = rc;
Jeff Garzikff03d492007-08-15 16:01:08 -0700259 }
Ben Hutchings01414802010-08-17 02:31:15 -0700260 if (ops && ops->get_regs_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 info.regdump_len = ops->get_regs_len(dev);
Ben Hutchings01414802010-08-17 02:31:15 -0700262 if (ops && ops->get_eeprom_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 info.eedump_len = ops->get_eeprom_len(dev);
264
265 if (copy_to_user(useraddr, &info, sizeof(info)))
266 return -EFAULT;
267 return 0;
268}
269
Eric Dumazetf5c445e2010-03-08 12:17:04 -0800270static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
chavey97f8aef2010-04-07 21:54:42 -0700271 void __user *useraddr)
Jeff Garzik723b2f52010-03-03 22:51:50 +0000272{
273 struct ethtool_sset_info info;
Jeff Garzik723b2f52010-03-03 22:51:50 +0000274 u64 sset_mask;
275 int i, idx = 0, n_bits = 0, ret, rc;
276 u32 *info_buf = NULL;
277
Jeff Garzik723b2f52010-03-03 22:51:50 +0000278 if (copy_from_user(&info, useraddr, sizeof(info)))
279 return -EFAULT;
280
281 /* store copy of mask, because we zero struct later on */
282 sset_mask = info.sset_mask;
283 if (!sset_mask)
284 return 0;
285
286 /* calculate size of return buffer */
Jeff Garzikd17792e2010-03-04 08:21:53 +0000287 n_bits = hweight64(sset_mask);
Jeff Garzik723b2f52010-03-03 22:51:50 +0000288
289 memset(&info, 0, sizeof(info));
290 info.cmd = ETHTOOL_GSSET_INFO;
291
292 info_buf = kzalloc(n_bits * sizeof(u32), GFP_USER);
293 if (!info_buf)
294 return -ENOMEM;
295
296 /*
297 * fill return buffer based on input bitmask and successful
298 * get_sset_count return
299 */
300 for (i = 0; i < 64; i++) {
301 if (!(sset_mask & (1ULL << i)))
302 continue;
303
Michał Mirosław340ae162011-02-15 16:59:16 +0000304 rc = __ethtool_get_sset_count(dev, i);
Jeff Garzik723b2f52010-03-03 22:51:50 +0000305 if (rc >= 0) {
306 info.sset_mask |= (1ULL << i);
307 info_buf[idx++] = rc;
308 }
309 }
310
311 ret = -EFAULT;
312 if (copy_to_user(useraddr, &info, sizeof(info)))
313 goto out;
314
315 useraddr += offsetof(struct ethtool_sset_info, data);
316 if (copy_to_user(useraddr, info_buf, idx * sizeof(u32)))
317 goto out;
318
319 ret = 0;
320
321out:
322 kfree(info_buf);
323 return ret;
324}
325
chavey97f8aef2010-04-07 21:54:42 -0700326static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
Ben Hutchingsbf988432010-06-28 08:45:58 +0000327 u32 cmd, void __user *useraddr)
Santwona Behera0853ad62008-07-02 03:47:41 -0700328{
Ben Hutchingsbf988432010-06-28 08:45:58 +0000329 struct ethtool_rxnfc info;
330 size_t info_size = sizeof(info);
Santwona Behera0853ad62008-07-02 03:47:41 -0700331
Santwona Behera59089d82009-02-20 00:58:13 -0800332 if (!dev->ethtool_ops->set_rxnfc)
Santwona Behera0853ad62008-07-02 03:47:41 -0700333 return -EOPNOTSUPP;
334
Ben Hutchingsbf988432010-06-28 08:45:58 +0000335 /* struct ethtool_rxnfc was originally defined for
336 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
337 * members. User-space might still be using that
338 * definition. */
339 if (cmd == ETHTOOL_SRXFH)
340 info_size = (offsetof(struct ethtool_rxnfc, data) +
341 sizeof(info.data));
342
343 if (copy_from_user(&info, useraddr, info_size))
Santwona Behera0853ad62008-07-02 03:47:41 -0700344 return -EFAULT;
345
Ben Hutchingsbf988432010-06-28 08:45:58 +0000346 return dev->ethtool_ops->set_rxnfc(dev, &info);
Santwona Behera0853ad62008-07-02 03:47:41 -0700347}
348
chavey97f8aef2010-04-07 21:54:42 -0700349static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
Ben Hutchingsbf988432010-06-28 08:45:58 +0000350 u32 cmd, void __user *useraddr)
Santwona Behera0853ad62008-07-02 03:47:41 -0700351{
352 struct ethtool_rxnfc info;
Ben Hutchingsbf988432010-06-28 08:45:58 +0000353 size_t info_size = sizeof(info);
Santwona Behera59089d82009-02-20 00:58:13 -0800354 const struct ethtool_ops *ops = dev->ethtool_ops;
355 int ret;
356 void *rule_buf = NULL;
Santwona Behera0853ad62008-07-02 03:47:41 -0700357
Santwona Behera59089d82009-02-20 00:58:13 -0800358 if (!ops->get_rxnfc)
Santwona Behera0853ad62008-07-02 03:47:41 -0700359 return -EOPNOTSUPP;
360
Ben Hutchingsbf988432010-06-28 08:45:58 +0000361 /* struct ethtool_rxnfc was originally defined for
362 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
363 * members. User-space might still be using that
364 * definition. */
365 if (cmd == ETHTOOL_GRXFH)
366 info_size = (offsetof(struct ethtool_rxnfc, data) +
367 sizeof(info.data));
368
369 if (copy_from_user(&info, useraddr, info_size))
Santwona Behera0853ad62008-07-02 03:47:41 -0700370 return -EFAULT;
371
Santwona Behera59089d82009-02-20 00:58:13 -0800372 if (info.cmd == ETHTOOL_GRXCLSRLALL) {
373 if (info.rule_cnt > 0) {
Ben Hutchingsdb048b62010-06-28 08:44:07 +0000374 if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
Kees Cookae6df5f2010-10-07 10:03:48 +0000375 rule_buf = kzalloc(info.rule_cnt * sizeof(u32),
Ben Hutchingsdb048b62010-06-28 08:44:07 +0000376 GFP_USER);
Santwona Behera59089d82009-02-20 00:58:13 -0800377 if (!rule_buf)
378 return -ENOMEM;
379 }
380 }
Santwona Behera0853ad62008-07-02 03:47:41 -0700381
Santwona Behera59089d82009-02-20 00:58:13 -0800382 ret = ops->get_rxnfc(dev, &info, rule_buf);
383 if (ret < 0)
384 goto err_out;
385
386 ret = -EFAULT;
Ben Hutchingsbf988432010-06-28 08:45:58 +0000387 if (copy_to_user(useraddr, &info, info_size))
Santwona Behera59089d82009-02-20 00:58:13 -0800388 goto err_out;
389
390 if (rule_buf) {
391 useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
392 if (copy_to_user(useraddr, rule_buf,
393 info.rule_cnt * sizeof(u32)))
394 goto err_out;
395 }
396 ret = 0;
397
398err_out:
Wei Yongjunc9cacec2009-03-31 15:06:26 -0700399 kfree(rule_buf);
Santwona Behera59089d82009-02-20 00:58:13 -0800400
401 return ret;
Santwona Behera0853ad62008-07-02 03:47:41 -0700402}
403
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000404static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
405 void __user *useraddr)
406{
407 struct ethtool_rxfh_indir *indir;
408 u32 table_size;
409 size_t full_size;
410 int ret;
411
412 if (!dev->ethtool_ops->get_rxfh_indir)
413 return -EOPNOTSUPP;
414
415 if (copy_from_user(&table_size,
416 useraddr + offsetof(struct ethtool_rxfh_indir, size),
417 sizeof(table_size)))
418 return -EFAULT;
419
420 if (table_size >
421 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
422 return -ENOMEM;
423 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
Kees Cookb00916b2010-10-11 12:23:25 -0700424 indir = kzalloc(full_size, GFP_USER);
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +0000425 if (!indir)
426 return -ENOMEM;
427
428 indir->cmd = ETHTOOL_GRXFHINDIR;
429 indir->size = table_size;
430 ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
431 if (ret)
432 goto out;
433
434 if (copy_to_user(useraddr, indir, full_size))
435 ret = -EFAULT;
436
437out:
438 kfree(indir);
439 return ret;
440}
441
442static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
443 void __user *useraddr)
444{
445 struct ethtool_rxfh_indir *indir;
446 u32 table_size;
447 size_t full_size;
448 int ret;
449
450 if (!dev->ethtool_ops->set_rxfh_indir)
451 return -EOPNOTSUPP;
452
453 if (copy_from_user(&table_size,
454 useraddr + offsetof(struct ethtool_rxfh_indir, size),
455 sizeof(table_size)))
456 return -EFAULT;
457
458 if (table_size >
459 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index))
460 return -ENOMEM;
461 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
462 indir = kmalloc(full_size, GFP_USER);
463 if (!indir)
464 return -ENOMEM;
465
466 if (copy_from_user(indir, useraddr, full_size)) {
467 ret = -EFAULT;
468 goto out;
469 }
470
471 ret = dev->ethtool_ops->set_rxfh_indir(dev, indir);
472
473out:
474 kfree(indir);
475 return ret;
476}
477
Peter Waskiewicze8589112010-02-12 13:48:05 +0000478static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list,
chavey97f8aef2010-04-07 21:54:42 -0700479 struct ethtool_rx_ntuple_flow_spec *spec,
480 struct ethtool_rx_ntuple_flow_spec_container *fsc)
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800481{
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800482
483 /* don't add filters forever */
Peter Waskiewicze8589112010-02-12 13:48:05 +0000484 if (list->count >= ETHTOOL_MAX_NTUPLE_LIST_ENTRY) {
485 /* free the container */
486 kfree(fsc);
487 return;
488 }
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800489
490 /* Copy the whole filter over */
491 fsc->fs.flow_type = spec->flow_type;
492 memcpy(&fsc->fs.h_u, &spec->h_u, sizeof(spec->h_u));
493 memcpy(&fsc->fs.m_u, &spec->m_u, sizeof(spec->m_u));
494
495 fsc->fs.vlan_tag = spec->vlan_tag;
496 fsc->fs.vlan_tag_mask = spec->vlan_tag_mask;
497 fsc->fs.data = spec->data;
498 fsc->fs.data_mask = spec->data_mask;
499 fsc->fs.action = spec->action;
500
501 /* add to the list */
502 list_add_tail_rcu(&fsc->list, &list->list);
503 list->count++;
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800504}
505
Ben Hutchingsbe2902d2010-09-16 11:28:07 +0000506/*
507 * ethtool does not (or did not) set masks for flow parameters that are
508 * not specified, so if both value and mask are 0 then this must be
509 * treated as equivalent to a mask with all bits set. Implement that
510 * here rather than in drivers.
511 */
512static void rx_ntuple_fix_masks(struct ethtool_rx_ntuple_flow_spec *fs)
513{
514 struct ethtool_tcpip4_spec *entry = &fs->h_u.tcp_ip4_spec;
515 struct ethtool_tcpip4_spec *mask = &fs->m_u.tcp_ip4_spec;
516
517 if (fs->flow_type != TCP_V4_FLOW &&
518 fs->flow_type != UDP_V4_FLOW &&
519 fs->flow_type != SCTP_V4_FLOW)
520 return;
521
522 if (!(entry->ip4src | mask->ip4src))
523 mask->ip4src = htonl(0xffffffff);
524 if (!(entry->ip4dst | mask->ip4dst))
525 mask->ip4dst = htonl(0xffffffff);
526 if (!(entry->psrc | mask->psrc))
527 mask->psrc = htons(0xffff);
528 if (!(entry->pdst | mask->pdst))
529 mask->pdst = htons(0xffff);
530 if (!(entry->tos | mask->tos))
531 mask->tos = 0xff;
532 if (!(fs->vlan_tag | fs->vlan_tag_mask))
533 fs->vlan_tag_mask = 0xffff;
534 if (!(fs->data | fs->data_mask))
535 fs->data_mask = 0xffffffffffffffffULL;
536}
537
chavey97f8aef2010-04-07 21:54:42 -0700538static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev,
539 void __user *useraddr)
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800540{
541 struct ethtool_rx_ntuple cmd;
542 const struct ethtool_ops *ops = dev->ethtool_ops;
Peter Waskiewicze8589112010-02-12 13:48:05 +0000543 struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL;
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800544 int ret;
545
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800546 if (!(dev->features & NETIF_F_NTUPLE))
547 return -EINVAL;
548
549 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
550 return -EFAULT;
551
Ben Hutchingsbe2902d2010-09-16 11:28:07 +0000552 rx_ntuple_fix_masks(&cmd.fs);
553
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800554 /*
555 * Cache filter in dev struct for GET operation only if
556 * the underlying driver doesn't have its own GET operation, and
Peter Waskiewicze8589112010-02-12 13:48:05 +0000557 * only if the filter was added successfully. First make sure we
558 * can allocate the filter, then continue if successful.
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800559 */
Peter Waskiewicze8589112010-02-12 13:48:05 +0000560 if (!ops->get_rx_ntuple) {
561 fsc = kmalloc(sizeof(*fsc), GFP_ATOMIC);
562 if (!fsc)
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800563 return -ENOMEM;
Peter Waskiewicze8589112010-02-12 13:48:05 +0000564 }
565
566 ret = ops->set_rx_ntuple(dev, &cmd);
567 if (ret) {
568 kfree(fsc);
569 return ret;
570 }
571
572 if (!ops->get_rx_ntuple)
573 __rx_ntuple_filter_add(&dev->ethtool_ntuple_list, &cmd.fs, fsc);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800574
575 return ret;
576}
577
578static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr)
579{
580 struct ethtool_gstrings gstrings;
581 const struct ethtool_ops *ops = dev->ethtool_ops;
582 struct ethtool_rx_ntuple_flow_spec_container *fsc;
583 u8 *data;
584 char *p;
585 int ret, i, num_strings = 0;
586
587 if (!ops->get_sset_count)
588 return -EOPNOTSUPP;
589
590 if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
591 return -EFAULT;
592
593 ret = ops->get_sset_count(dev, gstrings.string_set);
594 if (ret < 0)
595 return ret;
596
597 gstrings.len = ret;
598
Kees Cookb00916b2010-10-11 12:23:25 -0700599 data = kzalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800600 if (!data)
601 return -ENOMEM;
602
603 if (ops->get_rx_ntuple) {
604 /* driver-specific filter grab */
605 ret = ops->get_rx_ntuple(dev, gstrings.string_set, data);
606 goto copy;
607 }
608
609 /* default ethtool filter grab */
610 i = 0;
611 p = (char *)data;
612 list_for_each_entry(fsc, &dev->ethtool_ntuple_list.list, list) {
613 sprintf(p, "Filter %d:\n", i);
614 p += ETH_GSTRING_LEN;
615 num_strings++;
616
617 switch (fsc->fs.flow_type) {
618 case TCP_V4_FLOW:
619 sprintf(p, "\tFlow Type: TCP\n");
620 p += ETH_GSTRING_LEN;
621 num_strings++;
622 break;
623 case UDP_V4_FLOW:
624 sprintf(p, "\tFlow Type: UDP\n");
625 p += ETH_GSTRING_LEN;
626 num_strings++;
627 break;
628 case SCTP_V4_FLOW:
629 sprintf(p, "\tFlow Type: SCTP\n");
630 p += ETH_GSTRING_LEN;
631 num_strings++;
632 break;
633 case AH_ESP_V4_FLOW:
634 sprintf(p, "\tFlow Type: AH ESP\n");
635 p += ETH_GSTRING_LEN;
636 num_strings++;
637 break;
638 case ESP_V4_FLOW:
639 sprintf(p, "\tFlow Type: ESP\n");
640 p += ETH_GSTRING_LEN;
641 num_strings++;
642 break;
643 case IP_USER_FLOW:
644 sprintf(p, "\tFlow Type: Raw IP\n");
645 p += ETH_GSTRING_LEN;
646 num_strings++;
647 break;
648 case IPV4_FLOW:
649 sprintf(p, "\tFlow Type: IPv4\n");
650 p += ETH_GSTRING_LEN;
651 num_strings++;
652 break;
653 default:
654 sprintf(p, "\tFlow Type: Unknown\n");
655 p += ETH_GSTRING_LEN;
656 num_strings++;
657 goto unknown_filter;
Joe Perchesccbd6a52010-05-14 10:58:26 +0000658 }
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800659
660 /* now the rest of the filters */
661 switch (fsc->fs.flow_type) {
662 case TCP_V4_FLOW:
663 case UDP_V4_FLOW:
664 case SCTP_V4_FLOW:
665 sprintf(p, "\tSrc IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700666 fsc->fs.h_u.tcp_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800667 p += ETH_GSTRING_LEN;
668 num_strings++;
669 sprintf(p, "\tSrc IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700670 fsc->fs.m_u.tcp_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800671 p += ETH_GSTRING_LEN;
672 num_strings++;
673 sprintf(p, "\tDest IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700674 fsc->fs.h_u.tcp_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800675 p += ETH_GSTRING_LEN;
676 num_strings++;
677 sprintf(p, "\tDest IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700678 fsc->fs.m_u.tcp_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800679 p += ETH_GSTRING_LEN;
680 num_strings++;
681 sprintf(p, "\tSrc Port: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700682 fsc->fs.h_u.tcp_ip4_spec.psrc,
683 fsc->fs.m_u.tcp_ip4_spec.psrc);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800684 p += ETH_GSTRING_LEN;
685 num_strings++;
686 sprintf(p, "\tDest Port: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700687 fsc->fs.h_u.tcp_ip4_spec.pdst,
688 fsc->fs.m_u.tcp_ip4_spec.pdst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800689 p += ETH_GSTRING_LEN;
690 num_strings++;
691 sprintf(p, "\tTOS: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700692 fsc->fs.h_u.tcp_ip4_spec.tos,
693 fsc->fs.m_u.tcp_ip4_spec.tos);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800694 p += ETH_GSTRING_LEN;
695 num_strings++;
696 break;
697 case AH_ESP_V4_FLOW:
698 case ESP_V4_FLOW:
699 sprintf(p, "\tSrc IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700700 fsc->fs.h_u.ah_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800701 p += ETH_GSTRING_LEN;
702 num_strings++;
703 sprintf(p, "\tSrc IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700704 fsc->fs.m_u.ah_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800705 p += ETH_GSTRING_LEN;
706 num_strings++;
707 sprintf(p, "\tDest IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700708 fsc->fs.h_u.ah_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800709 p += ETH_GSTRING_LEN;
710 num_strings++;
711 sprintf(p, "\tDest IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700712 fsc->fs.m_u.ah_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800713 p += ETH_GSTRING_LEN;
714 num_strings++;
715 sprintf(p, "\tSPI: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700716 fsc->fs.h_u.ah_ip4_spec.spi,
717 fsc->fs.m_u.ah_ip4_spec.spi);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800718 p += ETH_GSTRING_LEN;
719 num_strings++;
720 sprintf(p, "\tTOS: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700721 fsc->fs.h_u.ah_ip4_spec.tos,
722 fsc->fs.m_u.ah_ip4_spec.tos);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800723 p += ETH_GSTRING_LEN;
724 num_strings++;
725 break;
726 case IP_USER_FLOW:
727 sprintf(p, "\tSrc IP addr: 0x%x\n",
Ben Hutchingse0de7c92010-09-14 09:13:08 +0000728 fsc->fs.h_u.usr_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800729 p += ETH_GSTRING_LEN;
730 num_strings++;
731 sprintf(p, "\tSrc IP mask: 0x%x\n",
Ben Hutchingse0de7c92010-09-14 09:13:08 +0000732 fsc->fs.m_u.usr_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800733 p += ETH_GSTRING_LEN;
734 num_strings++;
735 sprintf(p, "\tDest IP addr: 0x%x\n",
Ben Hutchingse0de7c92010-09-14 09:13:08 +0000736 fsc->fs.h_u.usr_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800737 p += ETH_GSTRING_LEN;
738 num_strings++;
739 sprintf(p, "\tDest IP mask: 0x%x\n",
Ben Hutchingse0de7c92010-09-14 09:13:08 +0000740 fsc->fs.m_u.usr_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800741 p += ETH_GSTRING_LEN;
742 num_strings++;
743 break;
744 case IPV4_FLOW:
745 sprintf(p, "\tSrc IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700746 fsc->fs.h_u.usr_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800747 p += ETH_GSTRING_LEN;
748 num_strings++;
749 sprintf(p, "\tSrc IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700750 fsc->fs.m_u.usr_ip4_spec.ip4src);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800751 p += ETH_GSTRING_LEN;
752 num_strings++;
753 sprintf(p, "\tDest IP addr: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700754 fsc->fs.h_u.usr_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800755 p += ETH_GSTRING_LEN;
756 num_strings++;
757 sprintf(p, "\tDest IP mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700758 fsc->fs.m_u.usr_ip4_spec.ip4dst);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800759 p += ETH_GSTRING_LEN;
760 num_strings++;
761 sprintf(p, "\tL4 bytes: 0x%x, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700762 fsc->fs.h_u.usr_ip4_spec.l4_4_bytes,
763 fsc->fs.m_u.usr_ip4_spec.l4_4_bytes);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800764 p += ETH_GSTRING_LEN;
765 num_strings++;
766 sprintf(p, "\tTOS: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700767 fsc->fs.h_u.usr_ip4_spec.tos,
768 fsc->fs.m_u.usr_ip4_spec.tos);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800769 p += ETH_GSTRING_LEN;
770 num_strings++;
771 sprintf(p, "\tIP Version: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700772 fsc->fs.h_u.usr_ip4_spec.ip_ver,
773 fsc->fs.m_u.usr_ip4_spec.ip_ver);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800774 p += ETH_GSTRING_LEN;
775 num_strings++;
776 sprintf(p, "\tProtocol: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700777 fsc->fs.h_u.usr_ip4_spec.proto,
778 fsc->fs.m_u.usr_ip4_spec.proto);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800779 p += ETH_GSTRING_LEN;
780 num_strings++;
781 break;
Joe Perchesccbd6a52010-05-14 10:58:26 +0000782 }
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800783 sprintf(p, "\tVLAN: %d, mask: 0x%x\n",
chavey97f8aef2010-04-07 21:54:42 -0700784 fsc->fs.vlan_tag, fsc->fs.vlan_tag_mask);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800785 p += ETH_GSTRING_LEN;
786 num_strings++;
787 sprintf(p, "\tUser-defined: 0x%Lx\n", fsc->fs.data);
788 p += ETH_GSTRING_LEN;
789 num_strings++;
790 sprintf(p, "\tUser-defined mask: 0x%Lx\n", fsc->fs.data_mask);
791 p += ETH_GSTRING_LEN;
792 num_strings++;
793 if (fsc->fs.action == ETHTOOL_RXNTUPLE_ACTION_DROP)
794 sprintf(p, "\tAction: Drop\n");
795 else
796 sprintf(p, "\tAction: Direct to queue %d\n",
chavey97f8aef2010-04-07 21:54:42 -0700797 fsc->fs.action);
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -0800798 p += ETH_GSTRING_LEN;
799 num_strings++;
800unknown_filter:
801 i++;
802 }
803copy:
804 /* indicate to userspace how many strings we actually have */
805 gstrings.len = num_strings;
806 ret = -EFAULT;
807 if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
808 goto out;
809 useraddr += sizeof(gstrings);
810 if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
811 goto out;
812 ret = 0;
813
814out:
815 kfree(data);
816 return ret;
817}
818
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
820{
821 struct ethtool_regs regs;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700822 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 void *regbuf;
824 int reglen, ret;
825
826 if (!ops->get_regs || !ops->get_regs_len)
827 return -EOPNOTSUPP;
828
829 if (copy_from_user(&regs, useraddr, sizeof(regs)))
830 return -EFAULT;
831
832 reglen = ops->get_regs_len(dev);
833 if (regs.len > reglen)
834 regs.len = reglen;
835
Eugene Teob7c7d012011-01-24 21:05:17 -0800836 regbuf = vzalloc(reglen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 if (!regbuf)
838 return -ENOMEM;
839
840 ops->get_regs(dev, &regs, regbuf);
841
842 ret = -EFAULT;
843 if (copy_to_user(useraddr, &regs, sizeof(regs)))
844 goto out;
845 useraddr += offsetof(struct ethtool_regs, data);
846 if (copy_to_user(useraddr, regbuf, regs.len))
847 goto out;
848 ret = 0;
849
850 out:
Ben Hutchingsa77f5db2010-09-20 08:42:17 +0000851 vfree(regbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 return ret;
853}
854
Ben Hutchingsd73d3a82009-10-05 10:59:58 +0000855static int ethtool_reset(struct net_device *dev, char __user *useraddr)
856{
857 struct ethtool_value reset;
858 int ret;
859
860 if (!dev->ethtool_ops->reset)
861 return -EOPNOTSUPP;
862
863 if (copy_from_user(&reset, useraddr, sizeof(reset)))
864 return -EFAULT;
865
866 ret = dev->ethtool_ops->reset(dev, &reset.data);
867 if (ret)
868 return ret;
869
870 if (copy_to_user(useraddr, &reset, sizeof(reset)))
871 return -EFAULT;
872 return 0;
873}
874
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
876{
Roland Dreier8e557422010-02-11 12:14:23 -0800877 struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
879 if (!dev->ethtool_ops->get_wol)
880 return -EOPNOTSUPP;
881
882 dev->ethtool_ops->get_wol(dev, &wol);
883
884 if (copy_to_user(useraddr, &wol, sizeof(wol)))
885 return -EFAULT;
886 return 0;
887}
888
889static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
890{
891 struct ethtool_wolinfo wol;
892
893 if (!dev->ethtool_ops->set_wol)
894 return -EOPNOTSUPP;
895
896 if (copy_from_user(&wol, useraddr, sizeof(wol)))
897 return -EFAULT;
898
899 return dev->ethtool_ops->set_wol(dev, &wol);
900}
901
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902static int ethtool_nway_reset(struct net_device *dev)
903{
904 if (!dev->ethtool_ops->nway_reset)
905 return -EOPNOTSUPP;
906
907 return dev->ethtool_ops->nway_reset(dev);
908}
909
Ben Hutchingse596e6e2010-12-09 12:08:35 +0000910static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
911{
912 struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
913
914 if (!dev->ethtool_ops->get_link)
915 return -EOPNOTSUPP;
916
917 edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev);
918
919 if (copy_to_user(useraddr, &edata, sizeof(edata)))
920 return -EFAULT;
921 return 0;
922}
923
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
925{
926 struct ethtool_eeprom eeprom;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700927 const struct ethtool_ops *ops = dev->ethtool_ops;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -0700928 void __user *userbuf = useraddr + sizeof(eeprom);
929 u32 bytes_remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 u8 *data;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -0700931 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
933 if (!ops->get_eeprom || !ops->get_eeprom_len)
934 return -EOPNOTSUPP;
935
936 if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
937 return -EFAULT;
938
939 /* Check for wrap and zero */
940 if (eeprom.offset + eeprom.len <= eeprom.offset)
941 return -EINVAL;
942
943 /* Check for exceeding total eeprom len */
944 if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
945 return -EINVAL;
946
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -0700947 data = kmalloc(PAGE_SIZE, GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 if (!data)
949 return -ENOMEM;
950
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -0700951 bytes_remaining = eeprom.len;
952 while (bytes_remaining > 0) {
953 eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -0700955 ret = ops->get_eeprom(dev, &eeprom, data);
956 if (ret)
957 break;
958 if (copy_to_user(userbuf, data, eeprom.len)) {
959 ret = -EFAULT;
960 break;
961 }
962 userbuf += eeprom.len;
963 eeprom.offset += eeprom.len;
964 bytes_remaining -= eeprom.len;
965 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Mandeep Singh Bainesc5835df2008-04-24 20:55:56 -0700967 eeprom.len = userbuf - (useraddr + sizeof(eeprom));
968 eeprom.offset -= eeprom.len;
969 if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
970 ret = -EFAULT;
971
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 kfree(data);
973 return ret;
974}
975
976static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
977{
978 struct ethtool_eeprom eeprom;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700979 const struct ethtool_ops *ops = dev->ethtool_ops;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -0700980 void __user *userbuf = useraddr + sizeof(eeprom);
981 u32 bytes_remaining;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 u8 *data;
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -0700983 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
985 if (!ops->set_eeprom || !ops->get_eeprom_len)
986 return -EOPNOTSUPP;
987
988 if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
989 return -EFAULT;
990
991 /* Check for wrap and zero */
992 if (eeprom.offset + eeprom.len <= eeprom.offset)
993 return -EINVAL;
994
995 /* Check for exceeding total eeprom len */
996 if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
997 return -EINVAL;
998
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -0700999 data = kmalloc(PAGE_SIZE, GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 if (!data)
1001 return -ENOMEM;
1002
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001003 bytes_remaining = eeprom.len;
1004 while (bytes_remaining > 0) {
1005 eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006
Mandeep Singh Bainesb131dd52008-04-15 19:24:17 -07001007 if (copy_from_user(data, userbuf, eeprom.len)) {
1008 ret = -EFAULT;
1009 break;
1010 }
1011 ret = ops->set_eeprom(dev, &eeprom, data);
1012 if (ret)
1013 break;
1014 userbuf += eeprom.len;
1015 eeprom.offset += eeprom.len;
1016 bytes_remaining -= eeprom.len;
1017 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 kfree(data);
1020 return ret;
1021}
1022
chavey97f8aef2010-04-07 21:54:42 -07001023static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev,
1024 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025{
Roland Dreier8e557422010-02-11 12:14:23 -08001026 struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
1028 if (!dev->ethtool_ops->get_coalesce)
1029 return -EOPNOTSUPP;
1030
1031 dev->ethtool_ops->get_coalesce(dev, &coalesce);
1032
1033 if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
1034 return -EFAULT;
1035 return 0;
1036}
1037
chavey97f8aef2010-04-07 21:54:42 -07001038static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
1039 void __user *useraddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040{
1041 struct ethtool_coalesce coalesce;
1042
David S. Millerfa04ae52005-06-06 15:07:19 -07001043 if (!dev->ethtool_ops->set_coalesce)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 return -EOPNOTSUPP;
1045
1046 if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
1047 return -EFAULT;
1048
1049 return dev->ethtool_ops->set_coalesce(dev, &coalesce);
1050}
1051
1052static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
1053{
Roland Dreier8e557422010-02-11 12:14:23 -08001054 struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055
1056 if (!dev->ethtool_ops->get_ringparam)
1057 return -EOPNOTSUPP;
1058
1059 dev->ethtool_ops->get_ringparam(dev, &ringparam);
1060
1061 if (copy_to_user(useraddr, &ringparam, sizeof(ringparam)))
1062 return -EFAULT;
1063 return 0;
1064}
1065
1066static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
1067{
1068 struct ethtool_ringparam ringparam;
1069
1070 if (!dev->ethtool_ops->set_ringparam)
1071 return -EOPNOTSUPP;
1072
1073 if (copy_from_user(&ringparam, useraddr, sizeof(ringparam)))
1074 return -EFAULT;
1075
1076 return dev->ethtool_ops->set_ringparam(dev, &ringparam);
1077}
1078
1079static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
1080{
1081 struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };
1082
1083 if (!dev->ethtool_ops->get_pauseparam)
1084 return -EOPNOTSUPP;
1085
1086 dev->ethtool_ops->get_pauseparam(dev, &pauseparam);
1087
1088 if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam)))
1089 return -EFAULT;
1090 return 0;
1091}
1092
1093static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
1094{
1095 struct ethtool_pauseparam pauseparam;
1096
Jeff Garzike1b90c42006-07-17 12:54:40 -04001097 if (!dev->ethtool_ops->set_pauseparam)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 return -EOPNOTSUPP;
1099
1100 if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))
1101 return -EFAULT;
1102
1103 return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
1104}
1105
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106static int __ethtool_set_sg(struct net_device *dev, u32 data)
1107{
1108 int err;
1109
1110 if (!data && dev->ethtool_ops->set_tso) {
1111 err = dev->ethtool_ops->set_tso(dev, 0);
1112 if (err)
1113 return err;
1114 }
1115
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001116 if (!data && dev->ethtool_ops->set_ufo) {
1117 err = dev->ethtool_ops->set_ufo(dev, 0);
1118 if (err)
1119 return err;
1120 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 return dev->ethtool_ops->set_sg(dev, data);
1122}
1123
1124static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr)
1125{
1126 struct ethtool_value edata;
1127 int err;
1128
1129 if (!dev->ethtool_ops->set_tx_csum)
1130 return -EOPNOTSUPP;
1131
1132 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1133 return -EFAULT;
1134
1135 if (!edata.data && dev->ethtool_ops->set_sg) {
1136 err = __ethtool_set_sg(dev, 0);
1137 if (err)
1138 return err;
1139 }
1140
1141 return dev->ethtool_ops->set_tx_csum(dev, edata.data);
1142}
1143
Herbert Xub240a0e2008-12-15 23:44:31 -08001144static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
1145{
1146 struct ethtool_value edata;
1147
1148 if (!dev->ethtool_ops->set_rx_csum)
1149 return -EOPNOTSUPP;
1150
1151 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1152 return -EFAULT;
1153
1154 if (!edata.data && dev->ethtool_ops->set_sg)
1155 dev->features &= ~NETIF_F_GRO;
1156
1157 return dev->ethtool_ops->set_rx_csum(dev, edata.data);
1158}
1159
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160static int ethtool_set_sg(struct net_device *dev, char __user *useraddr)
1161{
1162 struct ethtool_value edata;
1163
1164 if (!dev->ethtool_ops->set_sg)
1165 return -EOPNOTSUPP;
1166
1167 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1168 return -EFAULT;
1169
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09001170 if (edata.data &&
Herbert Xu8648b302006-06-17 22:06:05 -07001171 !(dev->features & NETIF_F_ALL_CSUM))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 return -EINVAL;
1173
1174 return __ethtool_set_sg(dev, edata.data);
1175}
1176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177static int ethtool_set_tso(struct net_device *dev, char __user *useraddr)
1178{
1179 struct ethtool_value edata;
1180
1181 if (!dev->ethtool_ops->set_tso)
1182 return -EOPNOTSUPP;
1183
1184 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1185 return -EFAULT;
1186
1187 if (edata.data && !(dev->features & NETIF_F_SG))
1188 return -EINVAL;
1189
1190 return dev->ethtool_ops->set_tso(dev, edata.data);
1191}
1192
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001193static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr)
1194{
1195 struct ethtool_value edata;
1196
1197 if (!dev->ethtool_ops->set_ufo)
1198 return -EOPNOTSUPP;
1199 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1200 return -EFAULT;
1201 if (edata.data && !(dev->features & NETIF_F_SG))
1202 return -EINVAL;
Michał Mirosław79032642010-11-30 06:38:00 +00001203 if (edata.data && !((dev->features & NETIF_F_GEN_CSUM) ||
1204 (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
1205 == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)))
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001206 return -EINVAL;
1207 return dev->ethtool_ops->set_ufo(dev, edata.data);
1208}
1209
Herbert Xu37c31852006-06-22 03:07:29 -07001210static int ethtool_get_gso(struct net_device *dev, char __user *useraddr)
1211{
1212 struct ethtool_value edata = { ETHTOOL_GGSO };
1213
1214 edata.data = dev->features & NETIF_F_GSO;
1215 if (copy_to_user(useraddr, &edata, sizeof(edata)))
chavey97f8aef2010-04-07 21:54:42 -07001216 return -EFAULT;
Herbert Xu37c31852006-06-22 03:07:29 -07001217 return 0;
1218}
1219
1220static int ethtool_set_gso(struct net_device *dev, char __user *useraddr)
1221{
1222 struct ethtool_value edata;
1223
1224 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1225 return -EFAULT;
1226 if (edata.data)
1227 dev->features |= NETIF_F_GSO;
1228 else
1229 dev->features &= ~NETIF_F_GSO;
1230 return 0;
1231}
1232
Herbert Xub240a0e2008-12-15 23:44:31 -08001233static int ethtool_get_gro(struct net_device *dev, char __user *useraddr)
1234{
1235 struct ethtool_value edata = { ETHTOOL_GGRO };
1236
1237 edata.data = dev->features & NETIF_F_GRO;
1238 if (copy_to_user(useraddr, &edata, sizeof(edata)))
chavey97f8aef2010-04-07 21:54:42 -07001239 return -EFAULT;
Herbert Xub240a0e2008-12-15 23:44:31 -08001240 return 0;
1241}
1242
1243static int ethtool_set_gro(struct net_device *dev, char __user *useraddr)
1244{
1245 struct ethtool_value edata;
1246
1247 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1248 return -EFAULT;
1249
1250 if (edata.data) {
Eric Dumazet67c96602010-09-17 11:56:18 -07001251 u32 rxcsum = dev->ethtool_ops->get_rx_csum ?
1252 dev->ethtool_ops->get_rx_csum(dev) :
1253 ethtool_op_get_rx_csum(dev);
1254
1255 if (!rxcsum)
Herbert Xub240a0e2008-12-15 23:44:31 -08001256 return -EINVAL;
1257 dev->features |= NETIF_F_GRO;
1258 } else
1259 dev->features &= ~NETIF_F_GRO;
1260
1261 return 0;
1262}
1263
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
1265{
1266 struct ethtool_test test;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001267 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 u64 *data;
Jeff Garzikff03d492007-08-15 16:01:08 -07001269 int ret, test_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001271 if (!ops->self_test || !ops->get_sset_count)
Jeff Garzikff03d492007-08-15 16:01:08 -07001272 return -EOPNOTSUPP;
1273
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001274 test_len = ops->get_sset_count(dev, ETH_SS_TEST);
Jeff Garzikff03d492007-08-15 16:01:08 -07001275 if (test_len < 0)
1276 return test_len;
1277 WARN_ON(test_len == 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278
1279 if (copy_from_user(&test, useraddr, sizeof(test)))
1280 return -EFAULT;
1281
Jeff Garzikff03d492007-08-15 16:01:08 -07001282 test.len = test_len;
1283 data = kmalloc(test_len * sizeof(u64), GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 if (!data)
1285 return -ENOMEM;
1286
1287 ops->self_test(dev, &test, data);
1288
1289 ret = -EFAULT;
1290 if (copy_to_user(useraddr, &test, sizeof(test)))
1291 goto out;
1292 useraddr += sizeof(test);
1293 if (copy_to_user(useraddr, data, test.len * sizeof(u64)))
1294 goto out;
1295 ret = 0;
1296
1297 out:
1298 kfree(data);
1299 return ret;
1300}
1301
1302static int ethtool_get_strings(struct net_device *dev, void __user *useraddr)
1303{
1304 struct ethtool_gstrings gstrings;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 u8 *data;
1306 int ret;
1307
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
1309 return -EFAULT;
1310
Michał Mirosław340ae162011-02-15 16:59:16 +00001311 ret = __ethtool_get_sset_count(dev, gstrings.string_set);
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001312 if (ret < 0)
1313 return ret;
Jeff Garzikff03d492007-08-15 16:01:08 -07001314
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001315 gstrings.len = ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
1317 data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
1318 if (!data)
1319 return -ENOMEM;
1320
Michał Mirosław340ae162011-02-15 16:59:16 +00001321 __ethtool_get_strings(dev, gstrings.string_set, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
1323 ret = -EFAULT;
1324 if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
1325 goto out;
1326 useraddr += sizeof(gstrings);
1327 if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
1328 goto out;
1329 ret = 0;
1330
Michał Mirosław340ae162011-02-15 16:59:16 +00001331out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 kfree(data);
1333 return ret;
1334}
1335
1336static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
1337{
1338 struct ethtool_value id;
1339
1340 if (!dev->ethtool_ops->phys_id)
1341 return -EOPNOTSUPP;
1342
1343 if (copy_from_user(&id, useraddr, sizeof(id)))
1344 return -EFAULT;
1345
1346 return dev->ethtool_ops->phys_id(dev, id.data);
1347}
1348
1349static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
1350{
1351 struct ethtool_stats stats;
Stephen Hemminger76fd8592006-09-08 11:16:13 -07001352 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 u64 *data;
Jeff Garzikff03d492007-08-15 16:01:08 -07001354 int ret, n_stats;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001356 if (!ops->get_ethtool_stats || !ops->get_sset_count)
Jeff Garzikff03d492007-08-15 16:01:08 -07001357 return -EOPNOTSUPP;
1358
Ben Hutchingsa9828ec2009-10-01 11:33:03 +00001359 n_stats = ops->get_sset_count(dev, ETH_SS_STATS);
Jeff Garzikff03d492007-08-15 16:01:08 -07001360 if (n_stats < 0)
1361 return n_stats;
1362 WARN_ON(n_stats == 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 if (copy_from_user(&stats, useraddr, sizeof(stats)))
1365 return -EFAULT;
1366
Jeff Garzikff03d492007-08-15 16:01:08 -07001367 stats.n_stats = n_stats;
1368 data = kmalloc(n_stats * sizeof(u64), GFP_USER);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 if (!data)
1370 return -ENOMEM;
1371
1372 ops->get_ethtool_stats(dev, &stats, data);
1373
1374 ret = -EFAULT;
1375 if (copy_to_user(useraddr, &stats, sizeof(stats)))
1376 goto out;
1377 useraddr += sizeof(stats);
1378 if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64)))
1379 goto out;
1380 ret = 0;
1381
1382 out:
1383 kfree(data);
1384 return ret;
1385}
1386
viro@ftp.linux.org.uk0bf0519d2005-09-05 03:26:18 +01001387static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
Jon Wetzela6f9a702005-08-20 17:15:54 -07001388{
1389 struct ethtool_perm_addr epaddr;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001390
Matthew Wilcox313674a2007-07-31 14:00:29 -07001391 if (copy_from_user(&epaddr, useraddr, sizeof(epaddr)))
Jon Wetzela6f9a702005-08-20 17:15:54 -07001392 return -EFAULT;
1393
Matthew Wilcox313674a2007-07-31 14:00:29 -07001394 if (epaddr.size < dev->addr_len)
1395 return -ETOOSMALL;
1396 epaddr.size = dev->addr_len;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001397
Jon Wetzela6f9a702005-08-20 17:15:54 -07001398 if (copy_to_user(useraddr, &epaddr, sizeof(epaddr)))
Matthew Wilcox313674a2007-07-31 14:00:29 -07001399 return -EFAULT;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001400 useraddr += sizeof(epaddr);
Matthew Wilcox313674a2007-07-31 14:00:29 -07001401 if (copy_to_user(useraddr, dev->perm_addr, epaddr.size))
1402 return -EFAULT;
1403 return 0;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001404}
1405
Jeff Garzik13c99b22007-08-15 16:01:56 -07001406static int ethtool_get_value(struct net_device *dev, char __user *useraddr,
1407 u32 cmd, u32 (*actor)(struct net_device *))
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001408{
Roland Dreier8e557422010-02-11 12:14:23 -08001409 struct ethtool_value edata = { .cmd = cmd };
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001410
Jeff Garzik13c99b22007-08-15 16:01:56 -07001411 if (!actor)
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001412 return -EOPNOTSUPP;
1413
Jeff Garzik13c99b22007-08-15 16:01:56 -07001414 edata.data = actor(dev);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001415
1416 if (copy_to_user(useraddr, &edata, sizeof(edata)))
1417 return -EFAULT;
1418 return 0;
1419}
1420
Jeff Garzik13c99b22007-08-15 16:01:56 -07001421static int ethtool_set_value_void(struct net_device *dev, char __user *useraddr,
1422 void (*actor)(struct net_device *, u32))
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001423{
1424 struct ethtool_value edata;
1425
Jeff Garzik13c99b22007-08-15 16:01:56 -07001426 if (!actor)
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001427 return -EOPNOTSUPP;
1428
1429 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1430 return -EFAULT;
1431
Jeff Garzik13c99b22007-08-15 16:01:56 -07001432 actor(dev, edata.data);
Jeff Garzik339bf022007-08-15 16:01:32 -07001433 return 0;
1434}
1435
Jeff Garzik13c99b22007-08-15 16:01:56 -07001436static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
1437 int (*actor)(struct net_device *, u32))
Jeff Garzik339bf022007-08-15 16:01:32 -07001438{
1439 struct ethtool_value edata;
1440
Jeff Garzik13c99b22007-08-15 16:01:56 -07001441 if (!actor)
Jeff Garzik339bf022007-08-15 16:01:32 -07001442 return -EOPNOTSUPP;
1443
1444 if (copy_from_user(&edata, useraddr, sizeof(edata)))
1445 return -EFAULT;
1446
Jeff Garzik13c99b22007-08-15 16:01:56 -07001447 return actor(dev, edata.data);
Jeff Garzik339bf022007-08-15 16:01:32 -07001448}
1449
chavey97f8aef2010-04-07 21:54:42 -07001450static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
1451 char __user *useraddr)
Ajit Khaparde05c6a8d2009-09-02 17:02:55 +00001452{
1453 struct ethtool_flash efl;
1454
1455 if (copy_from_user(&efl, useraddr, sizeof(efl)))
1456 return -EFAULT;
1457
1458 if (!dev->ethtool_ops->flash_device)
1459 return -EOPNOTSUPP;
1460
1461 return dev->ethtool_ops->flash_device(dev, &efl);
1462}
1463
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464/* The main entry point in this file. Called from net/core/dev.c */
1465
Eric W. Biederman881d9662007-09-17 11:56:21 -07001466int dev_ethtool(struct net *net, struct ifreq *ifr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467{
Eric W. Biederman881d9662007-09-17 11:56:21 -07001468 struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 void __user *useraddr = ifr->ifr_data;
1470 u32 ethcmd;
1471 int rc;
Michał Mirosław04ed3e72011-01-24 15:32:47 -08001472 u32 old_features;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 if (!dev || !netif_device_present(dev))
1475 return -ENODEV;
1476
chavey97f8aef2010-04-07 21:54:42 -07001477 if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 return -EFAULT;
1479
Ben Hutchings01414802010-08-17 02:31:15 -07001480 if (!dev->ethtool_ops) {
1481 /* ETHTOOL_GDRVINFO does not require any driver support.
1482 * It is also unprivileged and does not change anything,
1483 * so we can take a shortcut to it. */
1484 if (ethcmd == ETHTOOL_GDRVINFO)
1485 return ethtool_get_drvinfo(dev, useraddr);
1486 else
1487 return -EOPNOTSUPP;
1488 }
1489
Stephen Hemminger75f31232006-09-28 15:13:37 -07001490 /* Allow some commands to be done by anyone */
chavey97f8aef2010-04-07 21:54:42 -07001491 switch (ethcmd) {
stephen hemminger0fdc1002010-08-23 10:24:18 +00001492 case ETHTOOL_GSET:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001493 case ETHTOOL_GDRVINFO:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001494 case ETHTOOL_GMSGLVL:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001495 case ETHTOOL_GCOALESCE:
1496 case ETHTOOL_GRINGPARAM:
1497 case ETHTOOL_GPAUSEPARAM:
1498 case ETHTOOL_GRXCSUM:
1499 case ETHTOOL_GTXCSUM:
1500 case ETHTOOL_GSG:
1501 case ETHTOOL_GSTRINGS:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001502 case ETHTOOL_GTSO:
1503 case ETHTOOL_GPERMADDR:
1504 case ETHTOOL_GUFO:
1505 case ETHTOOL_GGSO:
stephen hemminger1cab8192010-02-11 13:48:29 +00001506 case ETHTOOL_GGRO:
Jeff Garzik339bf022007-08-15 16:01:32 -07001507 case ETHTOOL_GFLAGS:
1508 case ETHTOOL_GPFLAGS:
Santwona Behera0853ad62008-07-02 03:47:41 -07001509 case ETHTOOL_GRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08001510 case ETHTOOL_GRXRINGS:
1511 case ETHTOOL_GRXCLSRLCNT:
1512 case ETHTOOL_GRXCLSRULE:
1513 case ETHTOOL_GRXCLSRLALL:
Stephen Hemminger75f31232006-09-28 15:13:37 -07001514 break;
1515 default:
1516 if (!capable(CAP_NET_ADMIN))
1517 return -EPERM;
1518 }
1519
chavey97f8aef2010-04-07 21:54:42 -07001520 if (dev->ethtool_ops->begin) {
1521 rc = dev->ethtool_ops->begin(dev);
1522 if (rc < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 return rc;
chavey97f8aef2010-04-07 21:54:42 -07001524 }
Stephen Hemmingerd8a33ac2005-05-29 14:13:47 -07001525 old_features = dev->features;
1526
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 switch (ethcmd) {
1528 case ETHTOOL_GSET:
1529 rc = ethtool_get_settings(dev, useraddr);
1530 break;
1531 case ETHTOOL_SSET:
1532 rc = ethtool_set_settings(dev, useraddr);
1533 break;
1534 case ETHTOOL_GDRVINFO:
1535 rc = ethtool_get_drvinfo(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 break;
1537 case ETHTOOL_GREGS:
1538 rc = ethtool_get_regs(dev, useraddr);
1539 break;
1540 case ETHTOOL_GWOL:
1541 rc = ethtool_get_wol(dev, useraddr);
1542 break;
1543 case ETHTOOL_SWOL:
1544 rc = ethtool_set_wol(dev, useraddr);
1545 break;
1546 case ETHTOOL_GMSGLVL:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001547 rc = ethtool_get_value(dev, useraddr, ethcmd,
1548 dev->ethtool_ops->get_msglevel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 break;
1550 case ETHTOOL_SMSGLVL:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001551 rc = ethtool_set_value_void(dev, useraddr,
1552 dev->ethtool_ops->set_msglevel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 break;
1554 case ETHTOOL_NWAY_RST:
1555 rc = ethtool_nway_reset(dev);
1556 break;
1557 case ETHTOOL_GLINK:
Ben Hutchingse596e6e2010-12-09 12:08:35 +00001558 rc = ethtool_get_link(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 break;
1560 case ETHTOOL_GEEPROM:
1561 rc = ethtool_get_eeprom(dev, useraddr);
1562 break;
1563 case ETHTOOL_SEEPROM:
1564 rc = ethtool_set_eeprom(dev, useraddr);
1565 break;
1566 case ETHTOOL_GCOALESCE:
1567 rc = ethtool_get_coalesce(dev, useraddr);
1568 break;
1569 case ETHTOOL_SCOALESCE:
1570 rc = ethtool_set_coalesce(dev, useraddr);
1571 break;
1572 case ETHTOOL_GRINGPARAM:
1573 rc = ethtool_get_ringparam(dev, useraddr);
1574 break;
1575 case ETHTOOL_SRINGPARAM:
1576 rc = ethtool_set_ringparam(dev, useraddr);
1577 break;
1578 case ETHTOOL_GPAUSEPARAM:
1579 rc = ethtool_get_pauseparam(dev, useraddr);
1580 break;
1581 case ETHTOOL_SPAUSEPARAM:
1582 rc = ethtool_set_pauseparam(dev, useraddr);
1583 break;
1584 case ETHTOOL_GRXCSUM:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001585 rc = ethtool_get_value(dev, useraddr, ethcmd,
Sridhar Samudrala1896e612009-07-22 13:38:22 +00001586 (dev->ethtool_ops->get_rx_csum ?
1587 dev->ethtool_ops->get_rx_csum :
1588 ethtool_op_get_rx_csum));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 break;
1590 case ETHTOOL_SRXCSUM:
Herbert Xub240a0e2008-12-15 23:44:31 -08001591 rc = ethtool_set_rx_csum(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 break;
1593 case ETHTOOL_GTXCSUM:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001594 rc = ethtool_get_value(dev, useraddr, ethcmd,
Jeff Garzik88d3aaf2007-09-15 14:41:06 -07001595 (dev->ethtool_ops->get_tx_csum ?
1596 dev->ethtool_ops->get_tx_csum :
1597 ethtool_op_get_tx_csum));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 break;
1599 case ETHTOOL_STXCSUM:
1600 rc = ethtool_set_tx_csum(dev, useraddr);
1601 break;
1602 case ETHTOOL_GSG:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001603 rc = ethtool_get_value(dev, useraddr, ethcmd,
Jeff Garzik88d3aaf2007-09-15 14:41:06 -07001604 (dev->ethtool_ops->get_sg ?
1605 dev->ethtool_ops->get_sg :
1606 ethtool_op_get_sg));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 break;
1608 case ETHTOOL_SSG:
1609 rc = ethtool_set_sg(dev, useraddr);
1610 break;
1611 case ETHTOOL_GTSO:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001612 rc = ethtool_get_value(dev, useraddr, ethcmd,
Jeff Garzik88d3aaf2007-09-15 14:41:06 -07001613 (dev->ethtool_ops->get_tso ?
1614 dev->ethtool_ops->get_tso :
1615 ethtool_op_get_tso));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 break;
1617 case ETHTOOL_STSO:
1618 rc = ethtool_set_tso(dev, useraddr);
1619 break;
1620 case ETHTOOL_TEST:
1621 rc = ethtool_self_test(dev, useraddr);
1622 break;
1623 case ETHTOOL_GSTRINGS:
1624 rc = ethtool_get_strings(dev, useraddr);
1625 break;
1626 case ETHTOOL_PHYS_ID:
1627 rc = ethtool_phys_id(dev, useraddr);
1628 break;
1629 case ETHTOOL_GSTATS:
1630 rc = ethtool_get_stats(dev, useraddr);
1631 break;
Jon Wetzela6f9a702005-08-20 17:15:54 -07001632 case ETHTOOL_GPERMADDR:
1633 rc = ethtool_get_perm_addr(dev, useraddr);
1634 break;
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001635 case ETHTOOL_GUFO:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001636 rc = ethtool_get_value(dev, useraddr, ethcmd,
Jeff Garzik88d3aaf2007-09-15 14:41:06 -07001637 (dev->ethtool_ops->get_ufo ?
1638 dev->ethtool_ops->get_ufo :
1639 ethtool_op_get_ufo));
Ananda Rajue89e9cf2005-10-18 15:46:41 -07001640 break;
1641 case ETHTOOL_SUFO:
1642 rc = ethtool_set_ufo(dev, useraddr);
1643 break;
Herbert Xu37c31852006-06-22 03:07:29 -07001644 case ETHTOOL_GGSO:
1645 rc = ethtool_get_gso(dev, useraddr);
1646 break;
1647 case ETHTOOL_SGSO:
1648 rc = ethtool_set_gso(dev, useraddr);
1649 break;
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001650 case ETHTOOL_GFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001651 rc = ethtool_get_value(dev, useraddr, ethcmd,
Sridhar Samudrala1896e612009-07-22 13:38:22 +00001652 (dev->ethtool_ops->get_flags ?
1653 dev->ethtool_ops->get_flags :
1654 ethtool_op_get_flags));
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001655 break;
1656 case ETHTOOL_SFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001657 rc = ethtool_set_value(dev, useraddr,
1658 dev->ethtool_ops->set_flags);
Jeff Garzik3ae7c0b2007-08-15 16:00:51 -07001659 break;
Jeff Garzik339bf022007-08-15 16:01:32 -07001660 case ETHTOOL_GPFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001661 rc = ethtool_get_value(dev, useraddr, ethcmd,
1662 dev->ethtool_ops->get_priv_flags);
Jeff Garzik339bf022007-08-15 16:01:32 -07001663 break;
1664 case ETHTOOL_SPFLAGS:
Jeff Garzik13c99b22007-08-15 16:01:56 -07001665 rc = ethtool_set_value(dev, useraddr,
1666 dev->ethtool_ops->set_priv_flags);
Jeff Garzik339bf022007-08-15 16:01:32 -07001667 break;
Santwona Behera0853ad62008-07-02 03:47:41 -07001668 case ETHTOOL_GRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08001669 case ETHTOOL_GRXRINGS:
1670 case ETHTOOL_GRXCLSRLCNT:
1671 case ETHTOOL_GRXCLSRULE:
1672 case ETHTOOL_GRXCLSRLALL:
Ben Hutchingsbf988432010-06-28 08:45:58 +00001673 rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);
Santwona Behera0853ad62008-07-02 03:47:41 -07001674 break;
1675 case ETHTOOL_SRXFH:
Santwona Behera59089d82009-02-20 00:58:13 -08001676 case ETHTOOL_SRXCLSRLDEL:
1677 case ETHTOOL_SRXCLSRLINS:
Ben Hutchingsbf988432010-06-28 08:45:58 +00001678 rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
Santwona Behera0853ad62008-07-02 03:47:41 -07001679 break;
Herbert Xub240a0e2008-12-15 23:44:31 -08001680 case ETHTOOL_GGRO:
1681 rc = ethtool_get_gro(dev, useraddr);
1682 break;
1683 case ETHTOOL_SGRO:
1684 rc = ethtool_set_gro(dev, useraddr);
1685 break;
Ajit Khaparde05c6a8d2009-09-02 17:02:55 +00001686 case ETHTOOL_FLASHDEV:
1687 rc = ethtool_flash_device(dev, useraddr);
1688 break;
Ben Hutchingsd73d3a82009-10-05 10:59:58 +00001689 case ETHTOOL_RESET:
1690 rc = ethtool_reset(dev, useraddr);
1691 break;
Peter P Waskiewicz Jr15682bc2010-02-10 20:03:05 -08001692 case ETHTOOL_SRXNTUPLE:
1693 rc = ethtool_set_rx_ntuple(dev, useraddr);
1694 break;
1695 case ETHTOOL_GRXNTUPLE:
1696 rc = ethtool_get_rx_ntuple(dev, useraddr);
1697 break;
Jeff Garzik723b2f52010-03-03 22:51:50 +00001698 case ETHTOOL_GSSET_INFO:
1699 rc = ethtool_get_sset_info(dev, useraddr);
1700 break;
Ben Hutchingsa5b6ee22010-06-30 05:05:23 +00001701 case ETHTOOL_GRXFHINDIR:
1702 rc = ethtool_get_rxfh_indir(dev, useraddr);
1703 break;
1704 case ETHTOOL_SRXFHINDIR:
1705 rc = ethtool_set_rxfh_indir(dev, useraddr);
1706 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 default:
Matthew Wilcox61a44b92007-07-31 14:00:02 -07001708 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 }
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +09001710
Stephen Hemmingere71a4782007-04-10 20:10:33 -07001711 if (dev->ethtool_ops->complete)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 dev->ethtool_ops->complete(dev);
Stephen Hemmingerd8a33ac2005-05-29 14:13:47 -07001713
1714 if (old_features != dev->features)
1715 netdev_features_change(dev);
1716
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718}