ethtool.c 39.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
/*
 * net/core/ethtool.c - Ethtool ioctl handler
 * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
 *
 * This file is where we call all the ethtool_ops commands to get
6
 * the information ethtool needs.
Linus Torvalds's avatar
Linus Torvalds committed
7
 *
8
9
10
11
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
Linus Torvalds's avatar
Linus Torvalds committed
12
13
14
15
 */

#include <linux/module.h>
#include <linux/types.h>
16
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
17
18
19
#include <linux/errno.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
20
21
#include <linux/net_tstamp.h>
#include <linux/phy.h>
22
#include <linux/bitops.h>
23
#include <linux/uaccess.h>
24
#include <linux/vmalloc.h>
25
#include <linux/slab.h>
26
27
#include <linux/rtnetlink.h>
#include <linux/sched.h>
Linus Torvalds's avatar
Linus Torvalds committed
28

29
/*
Linus Torvalds's avatar
Linus Torvalds committed
30
31
32
33
34
35
36
37
38
 * Some useful ethtool_ops methods that're device independent.
 * If we find that all drivers want to do the same thing here,
 * we can turn these into dev_() function calls.
 */

u32 ethtool_op_get_link(struct net_device *dev)
{
	return netif_carrier_ok(dev) ? 1 : 0;
}
39
EXPORT_SYMBOL(ethtool_op_get_link);
Linus Torvalds's avatar
Linus Torvalds committed
40

41
42
43
44
45
46
47
48
49
50
51
int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
{
	info->so_timestamping =
		SOF_TIMESTAMPING_TX_SOFTWARE |
		SOF_TIMESTAMPING_RX_SOFTWARE |
		SOF_TIMESTAMPING_SOFTWARE;
	info->phc_index = -1;
	return 0;
}
EXPORT_SYMBOL(ethtool_op_get_ts_info);

Linus Torvalds's avatar
Linus Torvalds committed
52
53
/* Handlers for each ethtool command */

54
#define ETHTOOL_DEV_FEATURE_WORDS	((NETDEV_FEATURE_COUNT + 31) / 32)
55

56
57
58
59
60
61
62
static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
	[NETIF_F_SG_BIT] =               "tx-scatter-gather",
	[NETIF_F_IP_CSUM_BIT] =          "tx-checksum-ipv4",
	[NETIF_F_HW_CSUM_BIT] =          "tx-checksum-ip-generic",
	[NETIF_F_IPV6_CSUM_BIT] =        "tx-checksum-ipv6",
	[NETIF_F_HIGHDMA_BIT] =          "highdma",
	[NETIF_F_FRAGLIST_BIT] =         "tx-scatter-gather-fraglist",
63
	[NETIF_F_HW_VLAN_CTAG_TX_BIT] =  "tx-vlan-ctag-hw-insert",
64

65
66
	[NETIF_F_HW_VLAN_CTAG_RX_BIT] =  "rx-vlan-ctag-hw-parse",
	[NETIF_F_HW_VLAN_CTAG_FILTER_BIT] = "rx-vlan-ctag-filter",
67
68
69
	[NETIF_F_HW_VLAN_STAG_TX_BIT] =  "tx-vlan-stag-hw-insert",
	[NETIF_F_HW_VLAN_STAG_RX_BIT] =  "rx-vlan-stag-hw-parse",
	[NETIF_F_HW_VLAN_STAG_FILTER_BIT] = "rx-vlan-stag-filter",
70
71
72
73
74
75
76
77
78
79
80
81
82
	[NETIF_F_VLAN_CHALLENGED_BIT] =  "vlan-challenged",
	[NETIF_F_GSO_BIT] =              "tx-generic-segmentation",
	[NETIF_F_LLTX_BIT] =             "tx-lockless",
	[NETIF_F_NETNS_LOCAL_BIT] =      "netns-local",
	[NETIF_F_GRO_BIT] =              "rx-gro",
	[NETIF_F_LRO_BIT] =              "rx-lro",

	[NETIF_F_TSO_BIT] =              "tx-tcp-segmentation",
	[NETIF_F_UFO_BIT] =              "tx-udp-fragmentation",
	[NETIF_F_GSO_ROBUST_BIT] =       "tx-gso-robust",
	[NETIF_F_TSO_ECN_BIT] =          "tx-tcp-ecn-segmentation",
	[NETIF_F_TSO6_BIT] =             "tx-tcp6-segmentation",
	[NETIF_F_FSO_BIT] =              "tx-fcoe-segmentation",
83
	[NETIF_F_GSO_GRE_BIT] =		 "tx-gre-segmentation",
84
	[NETIF_F_GSO_UDP_TUNNEL_BIT] =	 "tx-udp_tnl-segmentation",
85
86
87
88
89
90
91
92
93

	[NETIF_F_FCOE_CRC_BIT] =         "tx-checksum-fcoe-crc",
	[NETIF_F_SCTP_CSUM_BIT] =        "tx-checksum-sctp",
	[NETIF_F_FCOE_MTU_BIT] =         "fcoe-mtu",
	[NETIF_F_NTUPLE_BIT] =           "rx-ntuple-filter",
	[NETIF_F_RXHASH_BIT] =           "rx-hashing",
	[NETIF_F_RXCSUM_BIT] =           "rx-checksum",
	[NETIF_F_NOCACHE_COPY_BIT] =     "tx-nocache-copy",
	[NETIF_F_LOOPBACK_BIT] =         "loopback",
Ben Greear's avatar
Ben Greear committed
94
	[NETIF_F_RXFCS_BIT] =            "rx-fcs",
Ben Greear's avatar
Ben Greear committed
95
	[NETIF_F_RXALL_BIT] =            "rx-all",
96
97
};

98
99
100
101
102
103
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
{
	struct ethtool_gfeatures cmd = {
		.cmd = ETHTOOL_GFEATURES,
		.size = ETHTOOL_DEV_FEATURE_WORDS,
	};
104
	struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
105
106
	u32 __user *sizeaddr;
	u32 copy_size;
107
108
109
	int i;

	/* in case feature bits run out again */
110
	BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) > sizeof(netdev_features_t));
111
112

	for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {
113
114
115
116
117
		features[i].available = (u32)(dev->hw_features >> (32 * i));
		features[i].requested = (u32)(dev->wanted_features >> (32 * i));
		features[i].active = (u32)(dev->features >> (32 * i));
		features[i].never_changed =
			(u32)(NETIF_F_NEVER_CHANGE >> (32 * i));
118
	}
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

	sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
	if (get_user(copy_size, sizeaddr))
		return -EFAULT;

	if (copy_size > ETHTOOL_DEV_FEATURE_WORDS)
		copy_size = ETHTOOL_DEV_FEATURE_WORDS;

	if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
		return -EFAULT;
	useraddr += sizeof(cmd);
	if (copy_to_user(useraddr, features, copy_size * sizeof(*features)))
		return -EFAULT;

	return 0;
}

static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
{
	struct ethtool_sfeatures cmd;
	struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
140
141
	netdev_features_t wanted = 0, valid = 0;
	int i, ret = 0;
142
143
144
145
146
147
148
149
150
151
152

	if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
		return -EFAULT;
	useraddr += sizeof(cmd);

	if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS)
		return -EINVAL;

	if (copy_from_user(features, useraddr, sizeof(features)))
		return -EFAULT;

153
	for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) {
154
155
		valid |= (netdev_features_t)features[i].valid << (32 * i);
		wanted |= (netdev_features_t)features[i].requested << (32 * i);
156
157
158
	}

	if (valid & ~NETIF_F_ETHTOOL_BITS)
159
160
		return -EINVAL;

161
162
	if (valid & ~dev->hw_features) {
		valid &= dev->hw_features;
163
164
165
		ret |= ETHTOOL_F_UNSUPPORTED;
	}

166
167
	dev->wanted_features &= ~valid;
	dev->wanted_features |= wanted & valid;
168
	__netdev_update_features(dev);
169

170
	if ((dev->wanted_features ^ dev->features) & valid)
171
172
173
174
175
		ret |= ETHTOOL_F_WISH;

	return ret;
}

176
177
178
179
static int __ethtool_get_sset_count(struct net_device *dev, int sset)
{
	const struct ethtool_ops *ops = dev->ethtool_ops;

180
181
182
	if (sset == ETH_SS_FEATURES)
		return ARRAY_SIZE(netdev_features_strings);

183
	if (ops->get_sset_count && ops->get_strings)
184
185
186
187
188
189
190
191
192
193
		return ops->get_sset_count(dev, sset);
	else
		return -EOPNOTSUPP;
}

static void __ethtool_get_strings(struct net_device *dev,
	u32 stringset, u8 *data)
{
	const struct ethtool_ops *ops = dev->ethtool_ops;

194
195
196
197
198
199
	if (stringset == ETH_SS_FEATURES)
		memcpy(data, netdev_features_strings,
			sizeof(netdev_features_strings));
	else
		/* ops->get_strings is valid because checked earlier */
		ops->get_strings(dev, stringset, data);
200
201
}

202
static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd)
203
204
205
206
207
208
209
{
	/* feature masks of legacy discrete ethtool ops */

	switch (eth_cmd) {
	case ETHTOOL_GTXCSUM:
	case ETHTOOL_STXCSUM:
		return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM;
210
211
212
	case ETHTOOL_GRXCSUM:
	case ETHTOOL_SRXCSUM:
		return NETIF_F_RXCSUM;
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
	case ETHTOOL_GSG:
	case ETHTOOL_SSG:
		return NETIF_F_SG;
	case ETHTOOL_GTSO:
	case ETHTOOL_STSO:
		return NETIF_F_ALL_TSO;
	case ETHTOOL_GUFO:
	case ETHTOOL_SUFO:
		return NETIF_F_UFO;
	case ETHTOOL_GGSO:
	case ETHTOOL_SGSO:
		return NETIF_F_GSO;
	case ETHTOOL_GGRO:
	case ETHTOOL_SGRO:
		return NETIF_F_GRO;
	default:
		BUG();
	}
}

static int ethtool_get_one_feature(struct net_device *dev,
	char __user *useraddr, u32 ethcmd)
{
236
	netdev_features_t mask = ethtool_get_feature_mask(ethcmd);
237
238
	struct ethtool_value edata = {
		.cmd = ethcmd,
239
		.data = !!(dev->features & mask),
240
241
242
243
244
245
246
247
248
249
250
	};

	if (copy_to_user(useraddr, &edata, sizeof(edata)))
		return -EFAULT;
	return 0;
}

static int ethtool_set_one_feature(struct net_device *dev,
	void __user *useraddr, u32 ethcmd)
{
	struct ethtool_value edata;
251
	netdev_features_t mask;
252
253
254
255

	if (copy_from_user(&edata, useraddr, sizeof(edata)))
		return -EFAULT;

256
257
	mask = ethtool_get_feature_mask(ethcmd);
	mask &= dev->hw_features;
258
259
	if (!mask)
		return -EOPNOTSUPP;
260

261
262
263
264
	if (edata.data)
		dev->wanted_features |= mask;
	else
		dev->wanted_features &= ~mask;
265

266
	__netdev_update_features(dev);
267

268
269
270
	return 0;
}

271
272
#define ETH_ALL_FLAGS    (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | \
			  ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH)
273
274
275
#define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_CTAG_RX | \
			  NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_NTUPLE | \
			  NETIF_F_RXHASH)
276
277
278

static u32 __ethtool_get_flags(struct net_device *dev)
{
279
280
	u32 flags = 0;

281
282
283
284
285
	if (dev->features & NETIF_F_LRO)	     flags |= ETH_FLAG_LRO;
	if (dev->features & NETIF_F_HW_VLAN_CTAG_RX) flags |= ETH_FLAG_RXVLAN;
	if (dev->features & NETIF_F_HW_VLAN_CTAG_TX) flags |= ETH_FLAG_TXVLAN;
	if (dev->features & NETIF_F_NTUPLE)	     flags |= ETH_FLAG_NTUPLE;
	if (dev->features & NETIF_F_RXHASH)	     flags |= ETH_FLAG_RXHASH;
286
287

	return flags;
288
289
}

290
static int __ethtool_set_flags(struct net_device *dev, u32 data)
291
{
292
	netdev_features_t features = 0, changed;
293

294
	if (data & ~ETH_ALL_FLAGS)
295
296
		return -EINVAL;

297
	if (data & ETH_FLAG_LRO)	features |= NETIF_F_LRO;
298
299
	if (data & ETH_FLAG_RXVLAN)	features |= NETIF_F_HW_VLAN_CTAG_RX;
	if (data & ETH_FLAG_TXVLAN)	features |= NETIF_F_HW_VLAN_CTAG_TX;
300
301
302
	if (data & ETH_FLAG_NTUPLE)	features |= NETIF_F_NTUPLE;
	if (data & ETH_FLAG_RXHASH)	features |= NETIF_F_RXHASH;

303
	/* allow changing only bits set in hw_features */
304
	changed = (features ^ dev->features) & ETH_ALL_FEATURES;
305
306
307
308
	if (changed & ~dev->hw_features)
		return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP;

	dev->wanted_features =
309
		(dev->wanted_features & ~changed) | (features & changed);
310

311
	__netdev_update_features(dev);
312
313
314
315

	return 0;
}

316
int __ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
Linus Torvalds's avatar
Linus Torvalds committed
317
{
318
	ASSERT_RTNL();
Linus Torvalds's avatar
Linus Torvalds committed
319

320
	if (!dev->ethtool_ops->get_settings)
Linus Torvalds's avatar
Linus Torvalds committed
321
322
		return -EOPNOTSUPP;

323
324
325
326
327
328
329
330
331
332
333
334
	memset(cmd, 0, sizeof(struct ethtool_cmd));
	cmd->cmd = ETHTOOL_GSET;
	return dev->ethtool_ops->get_settings(dev, cmd);
}
EXPORT_SYMBOL(__ethtool_get_settings);

static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
{
	int err;
	struct ethtool_cmd cmd;

	err = __ethtool_get_settings(dev, &cmd);
Linus Torvalds's avatar
Linus Torvalds committed
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
	if (err < 0)
		return err;

	if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
		return -EFAULT;
	return 0;
}

static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
{
	struct ethtool_cmd cmd;

	if (!dev->ethtool_ops->set_settings)
		return -EOPNOTSUPP;

	if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
		return -EFAULT;

	return dev->ethtool_ops->set_settings(dev, &cmd);
}

356
357
static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
						  void __user *useraddr)
Linus Torvalds's avatar
Linus Torvalds committed
358
359
{
	struct ethtool_drvinfo info;
360
	const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds's avatar
Linus Torvalds committed
361
362
363

	memset(&info, 0, sizeof(info));
	info.cmd = ETHTOOL_GDRVINFO;
364
	if (ops->get_drvinfo) {
365
366
367
368
369
370
371
372
373
		ops->get_drvinfo(dev, &info);
	} else if (dev->dev.parent && dev->dev.parent->driver) {
		strlcpy(info.bus_info, dev_name(dev->dev.parent),
			sizeof(info.bus_info));
		strlcpy(info.driver, dev->dev.parent->driver->name,
			sizeof(info.driver));
	} else {
		return -EOPNOTSUPP;
	}
Linus Torvalds's avatar
Linus Torvalds committed
374

375
376
	/*
	 * this method of obtaining string set info is deprecated;
377
	 * Use ETHTOOL_GSSET_INFO instead.
378
	 */
379
	if (ops->get_sset_count) {
380
381
382
383
384
385
386
387
		int rc;

		rc = ops->get_sset_count(dev, ETH_SS_TEST);
		if (rc >= 0)
			info.testinfo_len = rc;
		rc = ops->get_sset_count(dev, ETH_SS_STATS);
		if (rc >= 0)
			info.n_stats = rc;
388
389
390
		rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
		if (rc >= 0)
			info.n_priv_flags = rc;
391
	}
392
	if (ops->get_regs_len)
Linus Torvalds's avatar
Linus Torvalds committed
393
		info.regdump_len = ops->get_regs_len(dev);
394
	if (ops->get_eeprom_len)
Linus Torvalds's avatar
Linus Torvalds committed
395
396
397
398
399
400
401
		info.eedump_len = ops->get_eeprom_len(dev);

	if (copy_to_user(useraddr, &info, sizeof(info)))
		return -EFAULT;
	return 0;
}

Eric Dumazet's avatar
Eric Dumazet committed
402
static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
403
						    void __user *useraddr)
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
{
	struct ethtool_sset_info info;
	u64 sset_mask;
	int i, idx = 0, n_bits = 0, ret, rc;
	u32 *info_buf = NULL;

	if (copy_from_user(&info, useraddr, sizeof(info)))
		return -EFAULT;

	/* store copy of mask, because we zero struct later on */
	sset_mask = info.sset_mask;
	if (!sset_mask)
		return 0;

	/* calculate size of return buffer */
419
	n_bits = hweight64(sset_mask);
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435

	memset(&info, 0, sizeof(info));
	info.cmd = ETHTOOL_GSSET_INFO;

	info_buf = kzalloc(n_bits * sizeof(u32), GFP_USER);
	if (!info_buf)
		return -ENOMEM;

	/*
	 * fill return buffer based on input bitmask and successful
	 * get_sset_count return
	 */
	for (i = 0; i < 64; i++) {
		if (!(sset_mask & (1ULL << i)))
			continue;

436
		rc = __ethtool_get_sset_count(dev, i);
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
		if (rc >= 0) {
			info.sset_mask |= (1ULL << i);
			info_buf[idx++] = rc;
		}
	}

	ret = -EFAULT;
	if (copy_to_user(useraddr, &info, sizeof(info)))
		goto out;

	useraddr += offsetof(struct ethtool_sset_info, data);
	if (copy_to_user(useraddr, info_buf, idx * sizeof(u32)))
		goto out;

	ret = 0;

out:
	kfree(info_buf);
	return ret;
}

458
static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
459
						u32 cmd, void __user *useraddr)
460
{
461
462
	struct ethtool_rxnfc info;
	size_t info_size = sizeof(info);
463
	int rc;
464

465
	if (!dev->ethtool_ops->set_rxnfc)
466
467
		return -EOPNOTSUPP;

468
469
470
471
472
473
474
475
476
	/* struct ethtool_rxnfc was originally defined for
	 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
	 * members.  User-space might still be using that
	 * definition. */
	if (cmd == ETHTOOL_SRXFH)
		info_size = (offsetof(struct ethtool_rxnfc, data) +
			     sizeof(info.data));

	if (copy_from_user(&info, useraddr, info_size))
477
478
		return -EFAULT;

479
480
481
482
483
484
485
486
487
	rc = dev->ethtool_ops->set_rxnfc(dev, &info);
	if (rc)
		return rc;

	if (cmd == ETHTOOL_SRXCLSRLINS &&
	    copy_to_user(useraddr, &info, info_size))
		return -EFAULT;

	return 0;
488
489
}

490
static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
491
						u32 cmd, void __user *useraddr)
492
493
{
	struct ethtool_rxnfc info;
494
	size_t info_size = sizeof(info);
495
496
497
	const struct ethtool_ops *ops = dev->ethtool_ops;
	int ret;
	void *rule_buf = NULL;
498

499
	if (!ops->get_rxnfc)
500
501
		return -EOPNOTSUPP;

502
503
504
505
506
507
508
509
510
	/* struct ethtool_rxnfc was originally defined for
	 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
	 * members.  User-space might still be using that
	 * definition. */
	if (cmd == ETHTOOL_GRXFH)
		info_size = (offsetof(struct ethtool_rxnfc, data) +
			     sizeof(info.data));

	if (copy_from_user(&info, useraddr, info_size))
511
512
		return -EFAULT;

513
514
	if (info.cmd == ETHTOOL_GRXCLSRLALL) {
		if (info.rule_cnt > 0) {
515
			if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
516
				rule_buf = kzalloc(info.rule_cnt * sizeof(u32),
517
						   GFP_USER);
518
519
520
521
			if (!rule_buf)
				return -ENOMEM;
		}
	}
522

523
524
525
526
527
	ret = ops->get_rxnfc(dev, &info, rule_buf);
	if (ret < 0)
		goto err_out;

	ret = -EFAULT;
528
	if (copy_to_user(useraddr, &info, info_size))
529
530
531
532
533
534
535
536
537
538
539
		goto err_out;

	if (rule_buf) {
		useraddr += offsetof(struct ethtool_rxnfc, rule_locs);
		if (copy_to_user(useraddr, rule_buf,
				 info.rule_cnt * sizeof(u32)))
			goto err_out;
	}
	ret = 0;

err_out:
540
	kfree(rule_buf);
541
542

	return ret;
543
544
}

545
546
547
static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
						     void __user *useraddr)
{
548
549
	u32 user_size, dev_size;
	u32 *indir;
550
551
	int ret;

552
553
554
555
556
	if (!dev->ethtool_ops->get_rxfh_indir_size ||
	    !dev->ethtool_ops->get_rxfh_indir)
		return -EOPNOTSUPP;
	dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
	if (dev_size == 0)
557
558
		return -EOPNOTSUPP;

559
	if (copy_from_user(&user_size,
560
			   useraddr + offsetof(struct ethtool_rxfh_indir, size),
561
			   sizeof(user_size)))
562
563
		return -EFAULT;

564
565
566
567
568
569
570
571
572
573
574
575
	if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size),
			 &dev_size, sizeof(dev_size)))
		return -EFAULT;

	/* If the user buffer size is 0, this is just a query for the
	 * device table size.  Otherwise, if it's smaller than the
	 * device table size it's an error.
	 */
	if (user_size < dev_size)
		return user_size == 0 ? 0 : -EINVAL;

	indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
576
577
578
579
580
581
582
	if (!indir)
		return -ENOMEM;

	ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
	if (ret)
		goto out;

583
584
585
	if (copy_to_user(useraddr +
			 offsetof(struct ethtool_rxfh_indir, ring_index[0]),
			 indir, dev_size * sizeof(indir[0])))
586
587
588
589
590
591
592
593
594
595
		ret = -EFAULT;

out:
	kfree(indir);
	return ret;
}

static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
						     void __user *useraddr)
{
596
597
598
	struct ethtool_rxnfc rx_rings;
	u32 user_size, dev_size, i;
	u32 *indir;
599
	const struct ethtool_ops *ops = dev->ethtool_ops;
600
601
	int ret;

602
603
	if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir ||
	    !ops->get_rxnfc)
604
		return -EOPNOTSUPP;
605
606

	dev_size = ops->get_rxfh_indir_size(dev);
607
	if (dev_size == 0)
608
609
		return -EOPNOTSUPP;

610
	if (copy_from_user(&user_size,
611
			   useraddr + offsetof(struct ethtool_rxfh_indir, size),
612
			   sizeof(user_size)))
613
614
		return -EFAULT;

615
	if (user_size != 0 && user_size != dev_size)
616
617
618
		return -EINVAL;

	indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
619
620
621
	if (!indir)
		return -ENOMEM;

622
	rx_rings.cmd = ETHTOOL_GRXRINGS;
623
	ret = ops->get_rxnfc(dev, &rx_rings, NULL);
624
625
	if (ret)
		goto out;
626
627
628
629
630
631
632
633
634
635
636

	if (user_size == 0) {
		for (i = 0; i < dev_size; i++)
			indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
	} else {
		if (copy_from_user(indir,
				  useraddr +
				  offsetof(struct ethtool_rxfh_indir,
					   ring_index[0]),
				  dev_size * sizeof(indir[0]))) {
			ret = -EFAULT;
637
638
			goto out;
		}
639
640
641
642
643
644
645
646

		/* Validate ring indices */
		for (i = 0; i < dev_size; i++) {
			if (indir[i] >= rx_rings.data) {
				ret = -EINVAL;
				goto out;
			}
		}
647
648
	}

649
	ret = ops->set_rxfh_indir(dev, indir);
650
651
652
653
654
655

out:
	kfree(indir);
	return ret;
}

Linus Torvalds's avatar
Linus Torvalds committed
656
657
658
static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_regs regs;
659
	const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds's avatar
Linus Torvalds committed
660
661
662
663
664
665
666
667
668
669
670
671
672
	void *regbuf;
	int reglen, ret;

	if (!ops->get_regs || !ops->get_regs_len)
		return -EOPNOTSUPP;

	if (copy_from_user(&regs, useraddr, sizeof(regs)))
		return -EFAULT;

	reglen = ops->get_regs_len(dev);
	if (regs.len > reglen)
		regs.len = reglen;

673
	regbuf = vzalloc(reglen);
674
	if (reglen && !regbuf)
Linus Torvalds's avatar
Linus Torvalds committed
675
676
677
678
679
680
681
682
		return -ENOMEM;

	ops->get_regs(dev, &regs, regbuf);

	ret = -EFAULT;
	if (copy_to_user(useraddr, &regs, sizeof(regs)))
		goto out;
	useraddr += offsetof(struct ethtool_regs, data);
683
	if (regbuf && copy_to_user(useraddr, regbuf, regs.len))
Linus Torvalds's avatar
Linus Torvalds committed
684
685
686
687
		goto out;
	ret = 0;

 out:
688
	vfree(regbuf);
Linus Torvalds's avatar
Linus Torvalds committed
689
690
691
	return ret;
}

Ben Hutchings's avatar
Ben Hutchings committed
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
static int ethtool_reset(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_value reset;
	int ret;

	if (!dev->ethtool_ops->reset)
		return -EOPNOTSUPP;

	if (copy_from_user(&reset, useraddr, sizeof(reset)))
		return -EFAULT;

	ret = dev->ethtool_ops->reset(dev, &reset.data);
	if (ret)
		return ret;

	if (copy_to_user(useraddr, &reset, sizeof(reset)))
		return -EFAULT;
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
712
713
static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
{
714
	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
Linus Torvalds's avatar
Linus Torvalds committed
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738

	if (!dev->ethtool_ops->get_wol)
		return -EOPNOTSUPP;

	dev->ethtool_ops->get_wol(dev, &wol);

	if (copy_to_user(useraddr, &wol, sizeof(wol)))
		return -EFAULT;
	return 0;
}

static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_wolinfo wol;

	if (!dev->ethtool_ops->set_wol)
		return -EOPNOTSUPP;

	if (copy_from_user(&wol, useraddr, sizeof(wol)))
		return -EFAULT;

	return dev->ethtool_ops->set_wol(dev, &wol);
}

739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_eee edata;
	int rc;

	if (!dev->ethtool_ops->get_eee)
		return -EOPNOTSUPP;

	memset(&edata, 0, sizeof(struct ethtool_eee));
	edata.cmd = ETHTOOL_GEEE;
	rc = dev->ethtool_ops->get_eee(dev, &edata);

	if (rc)
		return rc;

	if (copy_to_user(useraddr, &edata, sizeof(edata)))
		return -EFAULT;

	return 0;
}

static int ethtool_set_eee(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_eee edata;

	if (!dev->ethtool_ops->set_eee)
		return -EOPNOTSUPP;

	if (copy_from_user(&edata, useraddr, sizeof(edata)))
		return -EFAULT;

	return dev->ethtool_ops->set_eee(dev, &edata);
}

Linus Torvalds's avatar
Linus Torvalds committed
773
774
775
776
777
778
779
780
static int ethtool_nway_reset(struct net_device *dev)
{
	if (!dev->ethtool_ops->nway_reset)
		return -EOPNOTSUPP;

	return dev->ethtool_ops->nway_reset(dev);
}

781
782
783
784
785
786
787
788
789
790
791
792
793
794
static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
{
	struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };

	if (!dev->ethtool_ops->get_link)
		return -EOPNOTSUPP;

	edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev);

	if (copy_to_user(useraddr, &edata, sizeof(edata)))
		return -EFAULT;
	return 0;
}

795
796
797
798
static int ethtool_get_any_eeprom(struct net_device *dev, void __user *useraddr,
				  int (*getter)(struct net_device *,
						struct ethtool_eeprom *, u8 *),
				  u32 total_len)
Linus Torvalds's avatar
Linus Torvalds committed
799
800
{
	struct ethtool_eeprom eeprom;
801
802
	void __user *userbuf = useraddr + sizeof(eeprom);
	u32 bytes_remaining;
Linus Torvalds's avatar
Linus Torvalds committed
803
	u8 *data;
804
	int ret = 0;
Linus Torvalds's avatar
Linus Torvalds committed
805
806
807
808
809
810
811
812
813

	if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
		return -EFAULT;

	/* Check for wrap and zero */
	if (eeprom.offset + eeprom.len <= eeprom.offset)
		return -EINVAL;

	/* Check for exceeding total eeprom len */
814
	if (eeprom.offset + eeprom.len > total_len)
Linus Torvalds's avatar
Linus Torvalds committed
815
816
		return -EINVAL;

817
	data = kmalloc(PAGE_SIZE, GFP_USER);
Linus Torvalds's avatar
Linus Torvalds committed
818
819
820
	if (!data)
		return -ENOMEM;

821
822
823
824
	bytes_remaining = eeprom.len;
	while (bytes_remaining > 0) {
		eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);

825
		ret = getter(dev, &eeprom, data);
826
827
828
829
830
831
832
833
834
835
		if (ret)
			break;
		if (copy_to_user(userbuf, data, eeprom.len)) {
			ret = -EFAULT;
			break;
		}
		userbuf += eeprom.len;
		eeprom.offset += eeprom.len;
		bytes_remaining -= eeprom.len;
	}
Linus Torvalds's avatar
Linus Torvalds committed
836

837
838
839
840
841
	eeprom.len = userbuf - (useraddr + sizeof(eeprom));
	eeprom.offset -= eeprom.len;
	if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
		ret = -EFAULT;

Linus Torvalds's avatar
Linus Torvalds committed
842
843
844
845
	kfree(data);
	return ret;
}

846
847
848
849
850
851
852
853
854
855
856
static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
{
	const struct ethtool_ops *ops = dev->ethtool_ops;

	if (!ops->get_eeprom || !ops->get_eeprom_len)
		return -EOPNOTSUPP;

	return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom,
				      ops->get_eeprom_len(dev));
}

Linus Torvalds's avatar
Linus Torvalds committed
857
858
859
static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
{
	struct ethtool_eeprom eeprom;
860
	const struct ethtool_ops *ops = dev->ethtool_ops;
861
862
	void __user *userbuf = useraddr + sizeof(eeprom);
	u32 bytes_remaining;
Linus Torvalds's avatar
Linus Torvalds committed
863
	u8 *data;
864
	int ret = 0;
Linus Torvalds's avatar
Linus Torvalds committed
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879

	if (!ops->set_eeprom || !ops->get_eeprom_len)
		return -EOPNOTSUPP;

	if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
		return -EFAULT;

	/* Check for wrap and zero */
	if (eeprom.offset + eeprom.len <= eeprom.offset)
		return -EINVAL;

	/* Check for exceeding total eeprom len */
	if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
		return -EINVAL;

880
	data = kmalloc(PAGE_SIZE, GFP_USER);
Linus Torvalds's avatar
Linus Torvalds committed
881
882
883
	if (!data)
		return -ENOMEM;

884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
	bytes_remaining = eeprom.len;
	while (bytes_remaining > 0) {
		eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);

		if (copy_from_user(data, userbuf, eeprom.len)) {
			ret = -EFAULT;
			break;
		}
		ret = ops->set_eeprom(dev, &eeprom, data);
		if (ret)
			break;
		userbuf += eeprom.len;
		eeprom.offset += eeprom.len;
		bytes_remaining -= eeprom.len;
	}
Linus Torvalds's avatar
Linus Torvalds committed
899
900
901
902
903

	kfree(data);
	return ret;
}

904
905
static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev,
						   void __user *useraddr)
Linus Torvalds's avatar
Linus Torvalds committed
906
{
907
	struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE };
Linus Torvalds's avatar
Linus Torvalds committed
908
909
910
911
912
913
914
915
916
917
918

	if (!dev->ethtool_ops->get_coalesce)
		return -EOPNOTSUPP;

	dev->ethtool_ops->get_coalesce(dev, &coalesce);

	if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
		return -EFAULT;
	return 0;
}

919
920
static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
						   void __user *useraddr)
Linus Torvalds's avatar
Linus Torvalds committed
921
922
923
{
	struct ethtool_coalesce coalesce;

924
	if (!dev->ethtool_ops->set_coalesce)
Linus Torvalds's avatar
Linus Torvalds committed
925
926
927
928
929
930
931
932
933
934
		return -EOPNOTSUPP;

	if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
		return -EFAULT;

	return dev->ethtool_ops->set_coalesce(dev, &coalesce);
}

static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
{
935
	struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM };
Linus Torvalds's avatar
Linus Torvalds committed
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959

	if (!dev->ethtool_ops->get_ringparam)
		return -EOPNOTSUPP;

	dev->ethtool_ops->get_ringparam(dev, &ringparam);

	if (copy_to_user(useraddr, &ringparam, sizeof(ringparam)))
		return -EFAULT;
	return 0;
}

static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
{
	struct ethtool_ringparam ringparam;

	if (!dev->ethtool_ops->set_ringparam)
		return -EOPNOTSUPP;

	if (copy_from_user(&ringparam, useraddr, sizeof(ringparam)))
		return -EFAULT;

	return dev->ethtool_ops->set_ringparam(dev, &ringparam);
}

960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
static noinline_for_stack int ethtool_get_channels(struct net_device *dev,
						   void __user *useraddr)
{
	struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS };

	if (!dev->ethtool_ops->get_channels)
		return -EOPNOTSUPP;

	dev->ethtool_ops->get_channels(dev, &channels);

	if (copy_to_user(useraddr, &channels, sizeof(channels)))
		return -EFAULT;
	return 0;
}

static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
						   void __user *useraddr)
{
	struct ethtool_channels channels;

	if (!dev->ethtool_ops->set_channels)
		return -EOPNOTSUPP;

	if (copy_from_user(&channels, useraddr, sizeof(channels)))
		return -EFAULT;

	return dev->ethtool_ops->set_channels(dev, &channels);
}

Linus Torvalds's avatar
Linus Torvalds committed
989
990
991
992
993
994
995
996
997
998
999
1000
static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
{
	struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };

	if (!dev->ethtool_ops->get_pauseparam)
		return -EOPNOTSUPP;

	dev->ethtool_ops->get_pauseparam(dev, &pauseparam);

	if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam)))
		return -EFAULT;
	return 0;
For faster browsing, not all history is shown. View entire blame