diff options
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r-- | net/core/ethtool.c | 927 |
1 files changed, 727 insertions, 200 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 8451ab481095..fd14116ad7f0 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -19,7 +19,10 @@ | |||
19 | #include <linux/netdevice.h> | 19 | #include <linux/netdevice.h> |
20 | #include <linux/bitops.h> | 20 | #include <linux/bitops.h> |
21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
22 | #include <linux/vmalloc.h> | ||
22 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/rtnetlink.h> | ||
25 | #include <linux/sched.h> | ||
23 | 26 | ||
24 | /* | 27 | /* |
25 | * Some useful ethtool_ops methods that're device independent. | 28 | * Some useful ethtool_ops methods that're device independent. |
@@ -33,12 +36,6 @@ u32 ethtool_op_get_link(struct net_device *dev) | |||
33 | } | 36 | } |
34 | EXPORT_SYMBOL(ethtool_op_get_link); | 37 | EXPORT_SYMBOL(ethtool_op_get_link); |
35 | 38 | ||
36 | u32 ethtool_op_get_rx_csum(struct net_device *dev) | ||
37 | { | ||
38 | return (dev->features & NETIF_F_ALL_CSUM) != 0; | ||
39 | } | ||
40 | EXPORT_SYMBOL(ethtool_op_get_rx_csum); | ||
41 | |||
42 | u32 ethtool_op_get_tx_csum(struct net_device *dev) | 39 | u32 ethtool_op_get_tx_csum(struct net_device *dev) |
43 | { | 40 | { |
44 | return (dev->features & NETIF_F_ALL_CSUM) != 0; | 41 | return (dev->features & NETIF_F_ALL_CSUM) != 0; |
@@ -54,6 +51,7 @@ int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) | |||
54 | 51 | ||
55 | return 0; | 52 | return 0; |
56 | } | 53 | } |
54 | EXPORT_SYMBOL(ethtool_op_set_tx_csum); | ||
57 | 55 | ||
58 | int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data) | 56 | int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data) |
59 | { | 57 | { |
@@ -131,7 +129,8 @@ EXPORT_SYMBOL(ethtool_op_set_ufo); | |||
131 | * NETIF_F_xxx values in include/linux/netdevice.h | 129 | * NETIF_F_xxx values in include/linux/netdevice.h |
132 | */ | 130 | */ |
133 | static const u32 flags_dup_features = | 131 | static const u32 flags_dup_features = |
134 | (ETH_FLAG_LRO | ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH); | 132 | (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE | |
133 | ETH_FLAG_RXHASH); | ||
135 | 134 | ||
136 | u32 ethtool_op_get_flags(struct net_device *dev) | 135 | u32 ethtool_op_get_flags(struct net_device *dev) |
137 | { | 136 | { |
@@ -144,9 +143,24 @@ u32 ethtool_op_get_flags(struct net_device *dev) | |||
144 | } | 143 | } |
145 | EXPORT_SYMBOL(ethtool_op_get_flags); | 144 | EXPORT_SYMBOL(ethtool_op_get_flags); |
146 | 145 | ||
146 | /* Check if device can enable (or disable) particular feature coded in "data" | ||
147 | * argument. Flags "supported" describe features that can be toggled by device. | ||
148 | * If feature can not be toggled, it state (enabled or disabled) must match | ||
149 | * hardcoded device features state, otherwise flags are marked as invalid. | ||
150 | */ | ||
151 | bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported) | ||
152 | { | ||
153 | u32 features = dev->features & flags_dup_features; | ||
154 | /* "data" can contain only flags_dup_features bits, | ||
155 | * see __ethtool_set_flags */ | ||
156 | |||
157 | return (features & ~supported) != (data & ~supported); | ||
158 | } | ||
159 | EXPORT_SYMBOL(ethtool_invalid_flags); | ||
160 | |||
147 | int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported) | 161 | int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported) |
148 | { | 162 | { |
149 | if (data & ~supported) | 163 | if (ethtool_invalid_flags(dev, data, supported)) |
150 | return -EINVAL; | 164 | return -EINVAL; |
151 | 165 | ||
152 | dev->features = ((dev->features & ~flags_dup_features) | | 166 | dev->features = ((dev->features & ~flags_dup_features) | |
@@ -169,6 +183,404 @@ EXPORT_SYMBOL(ethtool_ntuple_flush); | |||
169 | 183 | ||
170 | /* Handlers for each ethtool command */ | 184 | /* Handlers for each ethtool command */ |
171 | 185 | ||
186 | #define ETHTOOL_DEV_FEATURE_WORDS 1 | ||
187 | |||
188 | static void ethtool_get_features_compat(struct net_device *dev, | ||
189 | struct ethtool_get_features_block *features) | ||
190 | { | ||
191 | if (!dev->ethtool_ops) | ||
192 | return; | ||
193 | |||
194 | /* getting RX checksum */ | ||
195 | if (dev->ethtool_ops->get_rx_csum) | ||
196 | if (dev->ethtool_ops->get_rx_csum(dev)) | ||
197 | features[0].active |= NETIF_F_RXCSUM; | ||
198 | |||
199 | /* mark legacy-changeable features */ | ||
200 | if (dev->ethtool_ops->set_sg) | ||
201 | features[0].available |= NETIF_F_SG; | ||
202 | if (dev->ethtool_ops->set_tx_csum) | ||
203 | features[0].available |= NETIF_F_ALL_CSUM; | ||
204 | if (dev->ethtool_ops->set_tso) | ||
205 | features[0].available |= NETIF_F_ALL_TSO; | ||
206 | if (dev->ethtool_ops->set_rx_csum) | ||
207 | features[0].available |= NETIF_F_RXCSUM; | ||
208 | if (dev->ethtool_ops->set_flags) | ||
209 | features[0].available |= flags_dup_features; | ||
210 | } | ||
211 | |||
212 | static int ethtool_set_feature_compat(struct net_device *dev, | ||
213 | int (*legacy_set)(struct net_device *, u32), | ||
214 | struct ethtool_set_features_block *features, u32 mask) | ||
215 | { | ||
216 | u32 do_set; | ||
217 | |||
218 | if (!legacy_set) | ||
219 | return 0; | ||
220 | |||
221 | if (!(features[0].valid & mask)) | ||
222 | return 0; | ||
223 | |||
224 | features[0].valid &= ~mask; | ||
225 | |||
226 | do_set = !!(features[0].requested & mask); | ||
227 | |||
228 | if (legacy_set(dev, do_set) < 0) | ||
229 | netdev_info(dev, | ||
230 | "Legacy feature change (%s) failed for 0x%08x\n", | ||
231 | do_set ? "set" : "clear", mask); | ||
232 | |||
233 | return 1; | ||
234 | } | ||
235 | |||
236 | static int ethtool_set_flags_compat(struct net_device *dev, | ||
237 | int (*legacy_set)(struct net_device *, u32), | ||
238 | struct ethtool_set_features_block *features, u32 mask) | ||
239 | { | ||
240 | u32 value; | ||
241 | |||
242 | if (!legacy_set) | ||
243 | return 0; | ||
244 | |||
245 | if (!(features[0].valid & mask)) | ||
246 | return 0; | ||
247 | |||
248 | value = dev->features & ~features[0].valid; | ||
249 | value |= features[0].requested; | ||
250 | |||
251 | features[0].valid &= ~mask; | ||
252 | |||
253 | if (legacy_set(dev, value & mask) < 0) | ||
254 | netdev_info(dev, "Legacy flags change failed\n"); | ||
255 | |||
256 | return 1; | ||
257 | } | ||
258 | |||
259 | static int ethtool_set_features_compat(struct net_device *dev, | ||
260 | struct ethtool_set_features_block *features) | ||
261 | { | ||
262 | int compat; | ||
263 | |||
264 | if (!dev->ethtool_ops) | ||
265 | return 0; | ||
266 | |||
267 | compat = ethtool_set_feature_compat(dev, dev->ethtool_ops->set_sg, | ||
268 | features, NETIF_F_SG); | ||
269 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tx_csum, | ||
270 | features, NETIF_F_ALL_CSUM); | ||
271 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tso, | ||
272 | features, NETIF_F_ALL_TSO); | ||
273 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_rx_csum, | ||
274 | features, NETIF_F_RXCSUM); | ||
275 | compat |= ethtool_set_flags_compat(dev, dev->ethtool_ops->set_flags, | ||
276 | features, flags_dup_features); | ||
277 | |||
278 | return compat; | ||
279 | } | ||
280 | |||
281 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) | ||
282 | { | ||
283 | struct ethtool_gfeatures cmd = { | ||
284 | .cmd = ETHTOOL_GFEATURES, | ||
285 | .size = ETHTOOL_DEV_FEATURE_WORDS, | ||
286 | }; | ||
287 | struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS] = { | ||
288 | { | ||
289 | .available = dev->hw_features, | ||
290 | .requested = dev->wanted_features, | ||
291 | .active = dev->features, | ||
292 | .never_changed = NETIF_F_NEVER_CHANGE, | ||
293 | }, | ||
294 | }; | ||
295 | u32 __user *sizeaddr; | ||
296 | u32 copy_size; | ||
297 | |||
298 | ethtool_get_features_compat(dev, features); | ||
299 | |||
300 | sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size); | ||
301 | if (get_user(copy_size, sizeaddr)) | ||
302 | return -EFAULT; | ||
303 | |||
304 | if (copy_size > ETHTOOL_DEV_FEATURE_WORDS) | ||
305 | copy_size = ETHTOOL_DEV_FEATURE_WORDS; | ||
306 | |||
307 | if (copy_to_user(useraddr, &cmd, sizeof(cmd))) | ||
308 | return -EFAULT; | ||
309 | useraddr += sizeof(cmd); | ||
310 | if (copy_to_user(useraddr, features, copy_size * sizeof(*features))) | ||
311 | return -EFAULT; | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int ethtool_set_features(struct net_device *dev, void __user *useraddr) | ||
317 | { | ||
318 | struct ethtool_sfeatures cmd; | ||
319 | struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; | ||
320 | int ret = 0; | ||
321 | |||
322 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) | ||
323 | return -EFAULT; | ||
324 | useraddr += sizeof(cmd); | ||
325 | |||
326 | if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS) | ||
327 | return -EINVAL; | ||
328 | |||
329 | if (copy_from_user(features, useraddr, sizeof(features))) | ||
330 | return -EFAULT; | ||
331 | |||
332 | if (features[0].valid & ~NETIF_F_ETHTOOL_BITS) | ||
333 | return -EINVAL; | ||
334 | |||
335 | if (ethtool_set_features_compat(dev, features)) | ||
336 | ret |= ETHTOOL_F_COMPAT; | ||
337 | |||
338 | if (features[0].valid & ~dev->hw_features) { | ||
339 | features[0].valid &= dev->hw_features; | ||
340 | ret |= ETHTOOL_F_UNSUPPORTED; | ||
341 | } | ||
342 | |||
343 | dev->wanted_features &= ~features[0].valid; | ||
344 | dev->wanted_features |= features[0].valid & features[0].requested; | ||
345 | __netdev_update_features(dev); | ||
346 | |||
347 | if ((dev->wanted_features ^ dev->features) & features[0].valid) | ||
348 | ret |= ETHTOOL_F_WISH; | ||
349 | |||
350 | return ret; | ||
351 | } | ||
352 | |||
353 | static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GSTRING_LEN] = { | ||
354 | /* NETIF_F_SG */ "tx-scatter-gather", | ||
355 | /* NETIF_F_IP_CSUM */ "tx-checksum-ipv4", | ||
356 | /* NETIF_F_NO_CSUM */ "tx-checksum-unneeded", | ||
357 | /* NETIF_F_HW_CSUM */ "tx-checksum-ip-generic", | ||
358 | /* NETIF_F_IPV6_CSUM */ "tx-checksum-ipv6", | ||
359 | /* NETIF_F_HIGHDMA */ "highdma", | ||
360 | /* NETIF_F_FRAGLIST */ "tx-scatter-gather-fraglist", | ||
361 | /* NETIF_F_HW_VLAN_TX */ "tx-vlan-hw-insert", | ||
362 | |||
363 | /* NETIF_F_HW_VLAN_RX */ "rx-vlan-hw-parse", | ||
364 | /* NETIF_F_HW_VLAN_FILTER */ "rx-vlan-filter", | ||
365 | /* NETIF_F_VLAN_CHALLENGED */ "vlan-challenged", | ||
366 | /* NETIF_F_GSO */ "tx-generic-segmentation", | ||
367 | /* NETIF_F_LLTX */ "tx-lockless", | ||
368 | /* NETIF_F_NETNS_LOCAL */ "netns-local", | ||
369 | /* NETIF_F_GRO */ "rx-gro", | ||
370 | /* NETIF_F_LRO */ "rx-lro", | ||
371 | |||
372 | /* NETIF_F_TSO */ "tx-tcp-segmentation", | ||
373 | /* NETIF_F_UFO */ "tx-udp-fragmentation", | ||
374 | /* NETIF_F_GSO_ROBUST */ "tx-gso-robust", | ||
375 | /* NETIF_F_TSO_ECN */ "tx-tcp-ecn-segmentation", | ||
376 | /* NETIF_F_TSO6 */ "tx-tcp6-segmentation", | ||
377 | /* NETIF_F_FSO */ "tx-fcoe-segmentation", | ||
378 | "", | ||
379 | "", | ||
380 | |||
381 | /* NETIF_F_FCOE_CRC */ "tx-checksum-fcoe-crc", | ||
382 | /* NETIF_F_SCTP_CSUM */ "tx-checksum-sctp", | ||
383 | /* NETIF_F_FCOE_MTU */ "fcoe-mtu", | ||
384 | /* NETIF_F_NTUPLE */ "rx-ntuple-filter", | ||
385 | /* NETIF_F_RXHASH */ "rx-hashing", | ||
386 | /* NETIF_F_RXCSUM */ "rx-checksum", | ||
387 | /* NETIF_F_NOCACHE_COPY */ "tx-nocache-copy", | ||
388 | /* NETIF_F_LOOPBACK */ "loopback", | ||
389 | }; | ||
390 | |||
391 | static int __ethtool_get_sset_count(struct net_device *dev, int sset) | ||
392 | { | ||
393 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
394 | |||
395 | if (sset == ETH_SS_FEATURES) | ||
396 | return ARRAY_SIZE(netdev_features_strings); | ||
397 | |||
398 | if (ops && ops->get_sset_count && ops->get_strings) | ||
399 | return ops->get_sset_count(dev, sset); | ||
400 | else | ||
401 | return -EOPNOTSUPP; | ||
402 | } | ||
403 | |||
404 | static void __ethtool_get_strings(struct net_device *dev, | ||
405 | u32 stringset, u8 *data) | ||
406 | { | ||
407 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
408 | |||
409 | if (stringset == ETH_SS_FEATURES) | ||
410 | memcpy(data, netdev_features_strings, | ||
411 | sizeof(netdev_features_strings)); | ||
412 | else | ||
413 | /* ops->get_strings is valid because checked earlier */ | ||
414 | ops->get_strings(dev, stringset, data); | ||
415 | } | ||
416 | |||
417 | static u32 ethtool_get_feature_mask(u32 eth_cmd) | ||
418 | { | ||
419 | /* feature masks of legacy discrete ethtool ops */ | ||
420 | |||
421 | switch (eth_cmd) { | ||
422 | case ETHTOOL_GTXCSUM: | ||
423 | case ETHTOOL_STXCSUM: | ||
424 | return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM; | ||
425 | case ETHTOOL_GRXCSUM: | ||
426 | case ETHTOOL_SRXCSUM: | ||
427 | return NETIF_F_RXCSUM; | ||
428 | case ETHTOOL_GSG: | ||
429 | case ETHTOOL_SSG: | ||
430 | return NETIF_F_SG; | ||
431 | case ETHTOOL_GTSO: | ||
432 | case ETHTOOL_STSO: | ||
433 | return NETIF_F_ALL_TSO; | ||
434 | case ETHTOOL_GUFO: | ||
435 | case ETHTOOL_SUFO: | ||
436 | return NETIF_F_UFO; | ||
437 | case ETHTOOL_GGSO: | ||
438 | case ETHTOOL_SGSO: | ||
439 | return NETIF_F_GSO; | ||
440 | case ETHTOOL_GGRO: | ||
441 | case ETHTOOL_SGRO: | ||
442 | return NETIF_F_GRO; | ||
443 | default: | ||
444 | BUG(); | ||
445 | } | ||
446 | } | ||
447 | |||
448 | static void *__ethtool_get_one_feature_actor(struct net_device *dev, u32 ethcmd) | ||
449 | { | ||
450 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
451 | |||
452 | if (!ops) | ||
453 | return NULL; | ||
454 | |||
455 | switch (ethcmd) { | ||
456 | case ETHTOOL_GTXCSUM: | ||
457 | return ops->get_tx_csum; | ||
458 | case ETHTOOL_GRXCSUM: | ||
459 | return ops->get_rx_csum; | ||
460 | case ETHTOOL_SSG: | ||
461 | return ops->get_sg; | ||
462 | case ETHTOOL_STSO: | ||
463 | return ops->get_tso; | ||
464 | case ETHTOOL_SUFO: | ||
465 | return ops->get_ufo; | ||
466 | default: | ||
467 | return NULL; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | static u32 __ethtool_get_rx_csum_oldbug(struct net_device *dev) | ||
472 | { | ||
473 | return !!(dev->features & NETIF_F_ALL_CSUM); | ||
474 | } | ||
475 | |||
476 | static int ethtool_get_one_feature(struct net_device *dev, | ||
477 | char __user *useraddr, u32 ethcmd) | ||
478 | { | ||
479 | u32 mask = ethtool_get_feature_mask(ethcmd); | ||
480 | struct ethtool_value edata = { | ||
481 | .cmd = ethcmd, | ||
482 | .data = !!(dev->features & mask), | ||
483 | }; | ||
484 | |||
485 | /* compatibility with discrete get_ ops */ | ||
486 | if (!(dev->hw_features & mask)) { | ||
487 | u32 (*actor)(struct net_device *); | ||
488 | |||
489 | actor = __ethtool_get_one_feature_actor(dev, ethcmd); | ||
490 | |||
491 | /* bug compatibility with old get_rx_csum */ | ||
492 | if (ethcmd == ETHTOOL_GRXCSUM && !actor) | ||
493 | actor = __ethtool_get_rx_csum_oldbug; | ||
494 | |||
495 | if (actor) | ||
496 | edata.data = actor(dev); | ||
497 | } | ||
498 | |||
499 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | ||
500 | return -EFAULT; | ||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | static int __ethtool_set_tx_csum(struct net_device *dev, u32 data); | ||
505 | static int __ethtool_set_rx_csum(struct net_device *dev, u32 data); | ||
506 | static int __ethtool_set_sg(struct net_device *dev, u32 data); | ||
507 | static int __ethtool_set_tso(struct net_device *dev, u32 data); | ||
508 | static int __ethtool_set_ufo(struct net_device *dev, u32 data); | ||
509 | |||
510 | static int ethtool_set_one_feature(struct net_device *dev, | ||
511 | void __user *useraddr, u32 ethcmd) | ||
512 | { | ||
513 | struct ethtool_value edata; | ||
514 | u32 mask; | ||
515 | |||
516 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | ||
517 | return -EFAULT; | ||
518 | |||
519 | mask = ethtool_get_feature_mask(ethcmd); | ||
520 | mask &= dev->hw_features; | ||
521 | if (mask) { | ||
522 | if (edata.data) | ||
523 | dev->wanted_features |= mask; | ||
524 | else | ||
525 | dev->wanted_features &= ~mask; | ||
526 | |||
527 | __netdev_update_features(dev); | ||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | /* Driver is not converted to ndo_fix_features or does not | ||
532 | * support changing this offload. In the latter case it won't | ||
533 | * have corresponding ethtool_ops field set. | ||
534 | * | ||
535 | * Following part is to be removed after all drivers advertise | ||
536 | * their changeable features in netdev->hw_features and stop | ||
537 | * using discrete offload setting ops. | ||
538 | */ | ||
539 | |||
540 | switch (ethcmd) { | ||
541 | case ETHTOOL_STXCSUM: | ||
542 | return __ethtool_set_tx_csum(dev, edata.data); | ||
543 | case ETHTOOL_SRXCSUM: | ||
544 | return __ethtool_set_rx_csum(dev, edata.data); | ||
545 | case ETHTOOL_SSG: | ||
546 | return __ethtool_set_sg(dev, edata.data); | ||
547 | case ETHTOOL_STSO: | ||
548 | return __ethtool_set_tso(dev, edata.data); | ||
549 | case ETHTOOL_SUFO: | ||
550 | return __ethtool_set_ufo(dev, edata.data); | ||
551 | default: | ||
552 | return -EOPNOTSUPP; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | int __ethtool_set_flags(struct net_device *dev, u32 data) | ||
557 | { | ||
558 | u32 changed; | ||
559 | |||
560 | if (data & ~flags_dup_features) | ||
561 | return -EINVAL; | ||
562 | |||
563 | /* legacy set_flags() op */ | ||
564 | if (dev->ethtool_ops->set_flags) { | ||
565 | if (unlikely(dev->hw_features & flags_dup_features)) | ||
566 | netdev_warn(dev, | ||
567 | "driver BUG: mixed hw_features and set_flags()\n"); | ||
568 | return dev->ethtool_ops->set_flags(dev, data); | ||
569 | } | ||
570 | |||
571 | /* allow changing only bits set in hw_features */ | ||
572 | changed = (data ^ dev->features) & flags_dup_features; | ||
573 | if (changed & ~dev->hw_features) | ||
574 | return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP; | ||
575 | |||
576 | dev->wanted_features = | ||
577 | (dev->wanted_features & ~changed) | (data & dev->hw_features); | ||
578 | |||
579 | __netdev_update_features(dev); | ||
580 | |||
581 | return 0; | ||
582 | } | ||
583 | |||
172 | static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) | 584 | static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) |
173 | { | 585 | { |
174 | struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; | 586 | struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; |
@@ -205,18 +617,24 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, | |||
205 | struct ethtool_drvinfo info; | 617 | struct ethtool_drvinfo info; |
206 | const struct ethtool_ops *ops = dev->ethtool_ops; | 618 | const struct ethtool_ops *ops = dev->ethtool_ops; |
207 | 619 | ||
208 | if (!ops->get_drvinfo) | ||
209 | return -EOPNOTSUPP; | ||
210 | |||
211 | memset(&info, 0, sizeof(info)); | 620 | memset(&info, 0, sizeof(info)); |
212 | info.cmd = ETHTOOL_GDRVINFO; | 621 | info.cmd = ETHTOOL_GDRVINFO; |
213 | ops->get_drvinfo(dev, &info); | 622 | if (ops && ops->get_drvinfo) { |
623 | ops->get_drvinfo(dev, &info); | ||
624 | } else if (dev->dev.parent && dev->dev.parent->driver) { | ||
625 | strlcpy(info.bus_info, dev_name(dev->dev.parent), | ||
626 | sizeof(info.bus_info)); | ||
627 | strlcpy(info.driver, dev->dev.parent->driver->name, | ||
628 | sizeof(info.driver)); | ||
629 | } else { | ||
630 | return -EOPNOTSUPP; | ||
631 | } | ||
214 | 632 | ||
215 | /* | 633 | /* |
216 | * this method of obtaining string set info is deprecated; | 634 | * this method of obtaining string set info is deprecated; |
217 | * Use ETHTOOL_GSSET_INFO instead. | 635 | * Use ETHTOOL_GSSET_INFO instead. |
218 | */ | 636 | */ |
219 | if (ops->get_sset_count) { | 637 | if (ops && ops->get_sset_count) { |
220 | int rc; | 638 | int rc; |
221 | 639 | ||
222 | rc = ops->get_sset_count(dev, ETH_SS_TEST); | 640 | rc = ops->get_sset_count(dev, ETH_SS_TEST); |
@@ -229,9 +647,9 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, | |||
229 | if (rc >= 0) | 647 | if (rc >= 0) |
230 | info.n_priv_flags = rc; | 648 | info.n_priv_flags = rc; |
231 | } | 649 | } |
232 | if (ops->get_regs_len) | 650 | if (ops && ops->get_regs_len) |
233 | info.regdump_len = ops->get_regs_len(dev); | 651 | info.regdump_len = ops->get_regs_len(dev); |
234 | if (ops->get_eeprom_len) | 652 | if (ops && ops->get_eeprom_len) |
235 | info.eedump_len = ops->get_eeprom_len(dev); | 653 | info.eedump_len = ops->get_eeprom_len(dev); |
236 | 654 | ||
237 | if (copy_to_user(useraddr, &info, sizeof(info))) | 655 | if (copy_to_user(useraddr, &info, sizeof(info))) |
@@ -243,14 +661,10 @@ static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev, | |||
243 | void __user *useraddr) | 661 | void __user *useraddr) |
244 | { | 662 | { |
245 | struct ethtool_sset_info info; | 663 | struct ethtool_sset_info info; |
246 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
247 | u64 sset_mask; | 664 | u64 sset_mask; |
248 | int i, idx = 0, n_bits = 0, ret, rc; | 665 | int i, idx = 0, n_bits = 0, ret, rc; |
249 | u32 *info_buf = NULL; | 666 | u32 *info_buf = NULL; |
250 | 667 | ||
251 | if (!ops->get_sset_count) | ||
252 | return -EOPNOTSUPP; | ||
253 | |||
254 | if (copy_from_user(&info, useraddr, sizeof(info))) | 668 | if (copy_from_user(&info, useraddr, sizeof(info))) |
255 | return -EFAULT; | 669 | return -EFAULT; |
256 | 670 | ||
@@ -277,7 +691,7 @@ static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev, | |||
277 | if (!(sset_mask & (1ULL << i))) | 691 | if (!(sset_mask & (1ULL << i))) |
278 | continue; | 692 | continue; |
279 | 693 | ||
280 | rc = ops->get_sset_count(dev, i); | 694 | rc = __ethtool_get_sset_count(dev, i); |
281 | if (rc >= 0) { | 695 | if (rc >= 0) { |
282 | info.sset_mask |= (1ULL << i); | 696 | info.sset_mask |= (1ULL << i); |
283 | info_buf[idx++] = rc; | 697 | info_buf[idx++] = rc; |
@@ -479,6 +893,38 @@ static void __rx_ntuple_filter_add(struct ethtool_rx_ntuple_list *list, | |||
479 | list->count++; | 893 | list->count++; |
480 | } | 894 | } |
481 | 895 | ||
896 | /* | ||
897 | * ethtool does not (or did not) set masks for flow parameters that are | ||
898 | * not specified, so if both value and mask are 0 then this must be | ||
899 | * treated as equivalent to a mask with all bits set. Implement that | ||
900 | * here rather than in drivers. | ||
901 | */ | ||
902 | static void rx_ntuple_fix_masks(struct ethtool_rx_ntuple_flow_spec *fs) | ||
903 | { | ||
904 | struct ethtool_tcpip4_spec *entry = &fs->h_u.tcp_ip4_spec; | ||
905 | struct ethtool_tcpip4_spec *mask = &fs->m_u.tcp_ip4_spec; | ||
906 | |||
907 | if (fs->flow_type != TCP_V4_FLOW && | ||
908 | fs->flow_type != UDP_V4_FLOW && | ||
909 | fs->flow_type != SCTP_V4_FLOW) | ||
910 | return; | ||
911 | |||
912 | if (!(entry->ip4src | mask->ip4src)) | ||
913 | mask->ip4src = htonl(0xffffffff); | ||
914 | if (!(entry->ip4dst | mask->ip4dst)) | ||
915 | mask->ip4dst = htonl(0xffffffff); | ||
916 | if (!(entry->psrc | mask->psrc)) | ||
917 | mask->psrc = htons(0xffff); | ||
918 | if (!(entry->pdst | mask->pdst)) | ||
919 | mask->pdst = htons(0xffff); | ||
920 | if (!(entry->tos | mask->tos)) | ||
921 | mask->tos = 0xff; | ||
922 | if (!(fs->vlan_tag | fs->vlan_tag_mask)) | ||
923 | fs->vlan_tag_mask = 0xffff; | ||
924 | if (!(fs->data | fs->data_mask)) | ||
925 | fs->data_mask = 0xffffffffffffffffULL; | ||
926 | } | ||
927 | |||
482 | static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, | 928 | static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, |
483 | void __user *useraddr) | 929 | void __user *useraddr) |
484 | { | 930 | { |
@@ -487,12 +933,17 @@ static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev, | |||
487 | struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL; | 933 | struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL; |
488 | int ret; | 934 | int ret; |
489 | 935 | ||
936 | if (!ops->set_rx_ntuple) | ||
937 | return -EOPNOTSUPP; | ||
938 | |||
490 | if (!(dev->features & NETIF_F_NTUPLE)) | 939 | if (!(dev->features & NETIF_F_NTUPLE)) |
491 | return -EINVAL; | 940 | return -EINVAL; |
492 | 941 | ||
493 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) | 942 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) |
494 | return -EFAULT; | 943 | return -EFAULT; |
495 | 944 | ||
945 | rx_ntuple_fix_masks(&cmd.fs); | ||
946 | |||
496 | /* | 947 | /* |
497 | * Cache filter in dev struct for GET operation only if | 948 | * Cache filter in dev struct for GET operation only if |
498 | * the underlying driver doesn't have its own GET operation, and | 949 | * the underlying driver doesn't have its own GET operation, and |
@@ -667,19 +1118,19 @@ static int ethtool_get_rx_ntuple(struct net_device *dev, void __user *useraddr) | |||
667 | break; | 1118 | break; |
668 | case IP_USER_FLOW: | 1119 | case IP_USER_FLOW: |
669 | sprintf(p, "\tSrc IP addr: 0x%x\n", | 1120 | sprintf(p, "\tSrc IP addr: 0x%x\n", |
670 | fsc->fs.h_u.raw_ip4_spec.ip4src); | 1121 | fsc->fs.h_u.usr_ip4_spec.ip4src); |
671 | p += ETH_GSTRING_LEN; | 1122 | p += ETH_GSTRING_LEN; |
672 | num_strings++; | 1123 | num_strings++; |
673 | sprintf(p, "\tSrc IP mask: 0x%x\n", | 1124 | sprintf(p, "\tSrc IP mask: 0x%x\n", |
674 | fsc->fs.m_u.raw_ip4_spec.ip4src); | 1125 | fsc->fs.m_u.usr_ip4_spec.ip4src); |
675 | p += ETH_GSTRING_LEN; | 1126 | p += ETH_GSTRING_LEN; |
676 | num_strings++; | 1127 | num_strings++; |
677 | sprintf(p, "\tDest IP addr: 0x%x\n", | 1128 | sprintf(p, "\tDest IP addr: 0x%x\n", |
678 | fsc->fs.h_u.raw_ip4_spec.ip4dst); | 1129 | fsc->fs.h_u.usr_ip4_spec.ip4dst); |
679 | p += ETH_GSTRING_LEN; | 1130 | p += ETH_GSTRING_LEN; |
680 | num_strings++; | 1131 | num_strings++; |
681 | sprintf(p, "\tDest IP mask: 0x%x\n", | 1132 | sprintf(p, "\tDest IP mask: 0x%x\n", |
682 | fsc->fs.m_u.raw_ip4_spec.ip4dst); | 1133 | fsc->fs.m_u.usr_ip4_spec.ip4dst); |
683 | p += ETH_GSTRING_LEN; | 1134 | p += ETH_GSTRING_LEN; |
684 | num_strings++; | 1135 | num_strings++; |
685 | break; | 1136 | break; |
@@ -775,7 +1226,7 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) | |||
775 | if (regs.len > reglen) | 1226 | if (regs.len > reglen) |
776 | regs.len = reglen; | 1227 | regs.len = reglen; |
777 | 1228 | ||
778 | regbuf = kzalloc(reglen, GFP_USER); | 1229 | regbuf = vzalloc(reglen); |
779 | if (!regbuf) | 1230 | if (!regbuf) |
780 | return -ENOMEM; | 1231 | return -ENOMEM; |
781 | 1232 | ||
@@ -790,7 +1241,7 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) | |||
790 | ret = 0; | 1241 | ret = 0; |
791 | 1242 | ||
792 | out: | 1243 | out: |
793 | kfree(regbuf); | 1244 | vfree(regbuf); |
794 | return ret; | 1245 | return ret; |
795 | } | 1246 | } |
796 | 1247 | ||
@@ -849,6 +1300,20 @@ static int ethtool_nway_reset(struct net_device *dev) | |||
849 | return dev->ethtool_ops->nway_reset(dev); | 1300 | return dev->ethtool_ops->nway_reset(dev); |
850 | } | 1301 | } |
851 | 1302 | ||
1303 | static int ethtool_get_link(struct net_device *dev, char __user *useraddr) | ||
1304 | { | ||
1305 | struct ethtool_value edata = { .cmd = ETHTOOL_GLINK }; | ||
1306 | |||
1307 | if (!dev->ethtool_ops->get_link) | ||
1308 | return -EOPNOTSUPP; | ||
1309 | |||
1310 | edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev); | ||
1311 | |||
1312 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | ||
1313 | return -EFAULT; | ||
1314 | return 0; | ||
1315 | } | ||
1316 | |||
852 | static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr) | 1317 | static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr) |
853 | { | 1318 | { |
854 | struct ethtool_eeprom eeprom; | 1319 | struct ethtool_eeprom eeprom; |
@@ -1004,6 +1469,35 @@ static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr) | |||
1004 | return dev->ethtool_ops->set_ringparam(dev, &ringparam); | 1469 | return dev->ethtool_ops->set_ringparam(dev, &ringparam); |
1005 | } | 1470 | } |
1006 | 1471 | ||
1472 | static noinline_for_stack int ethtool_get_channels(struct net_device *dev, | ||
1473 | void __user *useraddr) | ||
1474 | { | ||
1475 | struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS }; | ||
1476 | |||
1477 | if (!dev->ethtool_ops->get_channels) | ||
1478 | return -EOPNOTSUPP; | ||
1479 | |||
1480 | dev->ethtool_ops->get_channels(dev, &channels); | ||
1481 | |||
1482 | if (copy_to_user(useraddr, &channels, sizeof(channels))) | ||
1483 | return -EFAULT; | ||
1484 | return 0; | ||
1485 | } | ||
1486 | |||
1487 | static noinline_for_stack int ethtool_set_channels(struct net_device *dev, | ||
1488 | void __user *useraddr) | ||
1489 | { | ||
1490 | struct ethtool_channels channels; | ||
1491 | |||
1492 | if (!dev->ethtool_ops->set_channels) | ||
1493 | return -EOPNOTSUPP; | ||
1494 | |||
1495 | if (copy_from_user(&channels, useraddr, sizeof(channels))) | ||
1496 | return -EFAULT; | ||
1497 | |||
1498 | return dev->ethtool_ops->set_channels(dev, &channels); | ||
1499 | } | ||
1500 | |||
1007 | static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr) | 1501 | static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr) |
1008 | { | 1502 | { |
1009 | struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM }; | 1503 | struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM }; |
@@ -1035,6 +1529,12 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data) | |||
1035 | { | 1529 | { |
1036 | int err; | 1530 | int err; |
1037 | 1531 | ||
1532 | if (!dev->ethtool_ops->set_sg) | ||
1533 | return -EOPNOTSUPP; | ||
1534 | |||
1535 | if (data && !(dev->features & NETIF_F_ALL_CSUM)) | ||
1536 | return -EINVAL; | ||
1537 | |||
1038 | if (!data && dev->ethtool_ops->set_tso) { | 1538 | if (!data && dev->ethtool_ops->set_tso) { |
1039 | err = dev->ethtool_ops->set_tso(dev, 0); | 1539 | err = dev->ethtool_ops->set_tso(dev, 0); |
1040 | if (err) | 1540 | if (err) |
@@ -1049,140 +1549,55 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data) | |||
1049 | return dev->ethtool_ops->set_sg(dev, data); | 1549 | return dev->ethtool_ops->set_sg(dev, data); |
1050 | } | 1550 | } |
1051 | 1551 | ||
1052 | static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr) | 1552 | static int __ethtool_set_tx_csum(struct net_device *dev, u32 data) |
1053 | { | 1553 | { |
1054 | struct ethtool_value edata; | ||
1055 | int err; | 1554 | int err; |
1056 | 1555 | ||
1057 | if (!dev->ethtool_ops->set_tx_csum) | 1556 | if (!dev->ethtool_ops->set_tx_csum) |
1058 | return -EOPNOTSUPP; | 1557 | return -EOPNOTSUPP; |
1059 | 1558 | ||
1060 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1559 | if (!data && dev->ethtool_ops->set_sg) { |
1061 | return -EFAULT; | ||
1062 | |||
1063 | if (!edata.data && dev->ethtool_ops->set_sg) { | ||
1064 | err = __ethtool_set_sg(dev, 0); | 1560 | err = __ethtool_set_sg(dev, 0); |
1065 | if (err) | 1561 | if (err) |
1066 | return err; | 1562 | return err; |
1067 | } | 1563 | } |
1068 | 1564 | ||
1069 | return dev->ethtool_ops->set_tx_csum(dev, edata.data); | 1565 | return dev->ethtool_ops->set_tx_csum(dev, data); |
1070 | } | 1566 | } |
1071 | EXPORT_SYMBOL(ethtool_op_set_tx_csum); | ||
1072 | 1567 | ||
1073 | static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr) | 1568 | static int __ethtool_set_rx_csum(struct net_device *dev, u32 data) |
1074 | { | 1569 | { |
1075 | struct ethtool_value edata; | ||
1076 | |||
1077 | if (!dev->ethtool_ops->set_rx_csum) | 1570 | if (!dev->ethtool_ops->set_rx_csum) |
1078 | return -EOPNOTSUPP; | 1571 | return -EOPNOTSUPP; |
1079 | 1572 | ||
1080 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1573 | if (!data) |
1081 | return -EFAULT; | ||
1082 | |||
1083 | if (!edata.data && dev->ethtool_ops->set_sg) | ||
1084 | dev->features &= ~NETIF_F_GRO; | 1574 | dev->features &= ~NETIF_F_GRO; |
1085 | 1575 | ||
1086 | return dev->ethtool_ops->set_rx_csum(dev, edata.data); | 1576 | return dev->ethtool_ops->set_rx_csum(dev, data); |
1087 | } | 1577 | } |
1088 | 1578 | ||
1089 | static int ethtool_set_sg(struct net_device *dev, char __user *useraddr) | 1579 | static int __ethtool_set_tso(struct net_device *dev, u32 data) |
1090 | { | 1580 | { |
1091 | struct ethtool_value edata; | ||
1092 | |||
1093 | if (!dev->ethtool_ops->set_sg) | ||
1094 | return -EOPNOTSUPP; | ||
1095 | |||
1096 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | ||
1097 | return -EFAULT; | ||
1098 | |||
1099 | if (edata.data && | ||
1100 | !(dev->features & NETIF_F_ALL_CSUM)) | ||
1101 | return -EINVAL; | ||
1102 | |||
1103 | return __ethtool_set_sg(dev, edata.data); | ||
1104 | } | ||
1105 | |||
1106 | static int ethtool_set_tso(struct net_device *dev, char __user *useraddr) | ||
1107 | { | ||
1108 | struct ethtool_value edata; | ||
1109 | |||
1110 | if (!dev->ethtool_ops->set_tso) | 1581 | if (!dev->ethtool_ops->set_tso) |
1111 | return -EOPNOTSUPP; | 1582 | return -EOPNOTSUPP; |
1112 | 1583 | ||
1113 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1584 | if (data && !(dev->features & NETIF_F_SG)) |
1114 | return -EFAULT; | ||
1115 | |||
1116 | if (edata.data && !(dev->features & NETIF_F_SG)) | ||
1117 | return -EINVAL; | 1585 | return -EINVAL; |
1118 | 1586 | ||
1119 | return dev->ethtool_ops->set_tso(dev, edata.data); | 1587 | return dev->ethtool_ops->set_tso(dev, data); |
1120 | } | 1588 | } |
1121 | 1589 | ||
1122 | static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr) | 1590 | static int __ethtool_set_ufo(struct net_device *dev, u32 data) |
1123 | { | 1591 | { |
1124 | struct ethtool_value edata; | ||
1125 | |||
1126 | if (!dev->ethtool_ops->set_ufo) | 1592 | if (!dev->ethtool_ops->set_ufo) |
1127 | return -EOPNOTSUPP; | 1593 | return -EOPNOTSUPP; |
1128 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1594 | if (data && !(dev->features & NETIF_F_SG)) |
1129 | return -EFAULT; | ||
1130 | if (edata.data && !(dev->features & NETIF_F_SG)) | ||
1131 | return -EINVAL; | 1595 | return -EINVAL; |
1132 | if (edata.data && !(dev->features & NETIF_F_HW_CSUM)) | 1596 | if (data && !((dev->features & NETIF_F_GEN_CSUM) || |
1597 | (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) | ||
1598 | == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) | ||
1133 | return -EINVAL; | 1599 | return -EINVAL; |
1134 | return dev->ethtool_ops->set_ufo(dev, edata.data); | 1600 | return dev->ethtool_ops->set_ufo(dev, data); |
1135 | } | ||
1136 | |||
1137 | static int ethtool_get_gso(struct net_device *dev, char __user *useraddr) | ||
1138 | { | ||
1139 | struct ethtool_value edata = { ETHTOOL_GGSO }; | ||
1140 | |||
1141 | edata.data = dev->features & NETIF_F_GSO; | ||
1142 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | ||
1143 | return -EFAULT; | ||
1144 | return 0; | ||
1145 | } | ||
1146 | |||
1147 | static int ethtool_set_gso(struct net_device *dev, char __user *useraddr) | ||
1148 | { | ||
1149 | struct ethtool_value edata; | ||
1150 | |||
1151 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | ||
1152 | return -EFAULT; | ||
1153 | if (edata.data) | ||
1154 | dev->features |= NETIF_F_GSO; | ||
1155 | else | ||
1156 | dev->features &= ~NETIF_F_GSO; | ||
1157 | return 0; | ||
1158 | } | ||
1159 | |||
1160 | static int ethtool_get_gro(struct net_device *dev, char __user *useraddr) | ||
1161 | { | ||
1162 | struct ethtool_value edata = { ETHTOOL_GGRO }; | ||
1163 | |||
1164 | edata.data = dev->features & NETIF_F_GRO; | ||
1165 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | ||
1166 | return -EFAULT; | ||
1167 | return 0; | ||
1168 | } | ||
1169 | |||
1170 | static int ethtool_set_gro(struct net_device *dev, char __user *useraddr) | ||
1171 | { | ||
1172 | struct ethtool_value edata; | ||
1173 | |||
1174 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | ||
1175 | return -EFAULT; | ||
1176 | |||
1177 | if (edata.data) { | ||
1178 | if (!dev->ethtool_ops->get_rx_csum || | ||
1179 | !dev->ethtool_ops->get_rx_csum(dev)) | ||
1180 | return -EINVAL; | ||
1181 | dev->features |= NETIF_F_GRO; | ||
1182 | } else | ||
1183 | dev->features &= ~NETIF_F_GRO; | ||
1184 | |||
1185 | return 0; | ||
1186 | } | 1601 | } |
1187 | 1602 | ||
1188 | static int ethtool_self_test(struct net_device *dev, char __user *useraddr) | 1603 | static int ethtool_self_test(struct net_device *dev, char __user *useraddr) |
@@ -1226,17 +1641,13 @@ static int ethtool_self_test(struct net_device *dev, char __user *useraddr) | |||
1226 | static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) | 1641 | static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) |
1227 | { | 1642 | { |
1228 | struct ethtool_gstrings gstrings; | 1643 | struct ethtool_gstrings gstrings; |
1229 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
1230 | u8 *data; | 1644 | u8 *data; |
1231 | int ret; | 1645 | int ret; |
1232 | 1646 | ||
1233 | if (!ops->get_strings || !ops->get_sset_count) | ||
1234 | return -EOPNOTSUPP; | ||
1235 | |||
1236 | if (copy_from_user(&gstrings, useraddr, sizeof(gstrings))) | 1647 | if (copy_from_user(&gstrings, useraddr, sizeof(gstrings))) |
1237 | return -EFAULT; | 1648 | return -EFAULT; |
1238 | 1649 | ||
1239 | ret = ops->get_sset_count(dev, gstrings.string_set); | 1650 | ret = __ethtool_get_sset_count(dev, gstrings.string_set); |
1240 | if (ret < 0) | 1651 | if (ret < 0) |
1241 | return ret; | 1652 | return ret; |
1242 | 1653 | ||
@@ -1246,7 +1657,7 @@ static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) | |||
1246 | if (!data) | 1657 | if (!data) |
1247 | return -ENOMEM; | 1658 | return -ENOMEM; |
1248 | 1659 | ||
1249 | ops->get_strings(dev, gstrings.string_set, data); | 1660 | __ethtool_get_strings(dev, gstrings.string_set, data); |
1250 | 1661 | ||
1251 | ret = -EFAULT; | 1662 | ret = -EFAULT; |
1252 | if (copy_to_user(useraddr, &gstrings, sizeof(gstrings))) | 1663 | if (copy_to_user(useraddr, &gstrings, sizeof(gstrings))) |
@@ -1256,7 +1667,7 @@ static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) | |||
1256 | goto out; | 1667 | goto out; |
1257 | ret = 0; | 1668 | ret = 0; |
1258 | 1669 | ||
1259 | out: | 1670 | out: |
1260 | kfree(data); | 1671 | kfree(data); |
1261 | return ret; | 1672 | return ret; |
1262 | } | 1673 | } |
@@ -1264,14 +1675,60 @@ static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) | |||
1264 | static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) | 1675 | static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) |
1265 | { | 1676 | { |
1266 | struct ethtool_value id; | 1677 | struct ethtool_value id; |
1678 | static bool busy; | ||
1679 | int rc; | ||
1267 | 1680 | ||
1268 | if (!dev->ethtool_ops->phys_id) | 1681 | if (!dev->ethtool_ops->set_phys_id) |
1269 | return -EOPNOTSUPP; | 1682 | return -EOPNOTSUPP; |
1270 | 1683 | ||
1684 | if (busy) | ||
1685 | return -EBUSY; | ||
1686 | |||
1271 | if (copy_from_user(&id, useraddr, sizeof(id))) | 1687 | if (copy_from_user(&id, useraddr, sizeof(id))) |
1272 | return -EFAULT; | 1688 | return -EFAULT; |
1273 | 1689 | ||
1274 | return dev->ethtool_ops->phys_id(dev, id.data); | 1690 | rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE); |
1691 | if (rc < 0) | ||
1692 | return rc; | ||
1693 | |||
1694 | /* Drop the RTNL lock while waiting, but prevent reentry or | ||
1695 | * removal of the device. | ||
1696 | */ | ||
1697 | busy = true; | ||
1698 | dev_hold(dev); | ||
1699 | rtnl_unlock(); | ||
1700 | |||
1701 | if (rc == 0) { | ||
1702 | /* Driver will handle this itself */ | ||
1703 | schedule_timeout_interruptible( | ||
1704 | id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT); | ||
1705 | } else { | ||
1706 | /* Driver expects to be called at twice the frequency in rc */ | ||
1707 | int n = rc * 2, i, interval = HZ / n; | ||
1708 | |||
1709 | /* Count down seconds */ | ||
1710 | do { | ||
1711 | /* Count down iterations per second */ | ||
1712 | i = n; | ||
1713 | do { | ||
1714 | rtnl_lock(); | ||
1715 | rc = dev->ethtool_ops->set_phys_id(dev, | ||
1716 | (i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON); | ||
1717 | rtnl_unlock(); | ||
1718 | if (rc) | ||
1719 | break; | ||
1720 | schedule_timeout_interruptible(interval); | ||
1721 | } while (!signal_pending(current) && --i != 0); | ||
1722 | } while (!signal_pending(current) && | ||
1723 | (id.data == 0 || --id.data != 0)); | ||
1724 | } | ||
1725 | |||
1726 | rtnl_lock(); | ||
1727 | dev_put(dev); | ||
1728 | busy = false; | ||
1729 | |||
1730 | (void)dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE); | ||
1731 | return rc; | ||
1275 | } | 1732 | } |
1276 | 1733 | ||
1277 | static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) | 1734 | static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) |
@@ -1389,6 +1846,87 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev, | |||
1389 | return dev->ethtool_ops->flash_device(dev, &efl); | 1846 | return dev->ethtool_ops->flash_device(dev, &efl); |
1390 | } | 1847 | } |
1391 | 1848 | ||
1849 | static int ethtool_set_dump(struct net_device *dev, | ||
1850 | void __user *useraddr) | ||
1851 | { | ||
1852 | struct ethtool_dump dump; | ||
1853 | |||
1854 | if (!dev->ethtool_ops->set_dump) | ||
1855 | return -EOPNOTSUPP; | ||
1856 | |||
1857 | if (copy_from_user(&dump, useraddr, sizeof(dump))) | ||
1858 | return -EFAULT; | ||
1859 | |||
1860 | return dev->ethtool_ops->set_dump(dev, &dump); | ||
1861 | } | ||
1862 | |||
1863 | static int ethtool_get_dump_flag(struct net_device *dev, | ||
1864 | void __user *useraddr) | ||
1865 | { | ||
1866 | int ret; | ||
1867 | struct ethtool_dump dump; | ||
1868 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
1869 | |||
1870 | if (!dev->ethtool_ops->get_dump_flag) | ||
1871 | return -EOPNOTSUPP; | ||
1872 | |||
1873 | if (copy_from_user(&dump, useraddr, sizeof(dump))) | ||
1874 | return -EFAULT; | ||
1875 | |||
1876 | ret = ops->get_dump_flag(dev, &dump); | ||
1877 | if (ret) | ||
1878 | return ret; | ||
1879 | |||
1880 | if (copy_to_user(useraddr, &dump, sizeof(dump))) | ||
1881 | return -EFAULT; | ||
1882 | return 0; | ||
1883 | } | ||
1884 | |||
1885 | static int ethtool_get_dump_data(struct net_device *dev, | ||
1886 | void __user *useraddr) | ||
1887 | { | ||
1888 | int ret; | ||
1889 | __u32 len; | ||
1890 | struct ethtool_dump dump, tmp; | ||
1891 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
1892 | void *data = NULL; | ||
1893 | |||
1894 | if (!dev->ethtool_ops->get_dump_data || | ||
1895 | !dev->ethtool_ops->get_dump_flag) | ||
1896 | return -EOPNOTSUPP; | ||
1897 | |||
1898 | if (copy_from_user(&dump, useraddr, sizeof(dump))) | ||
1899 | return -EFAULT; | ||
1900 | |||
1901 | memset(&tmp, 0, sizeof(tmp)); | ||
1902 | tmp.cmd = ETHTOOL_GET_DUMP_FLAG; | ||
1903 | ret = ops->get_dump_flag(dev, &tmp); | ||
1904 | if (ret) | ||
1905 | return ret; | ||
1906 | |||
1907 | len = (tmp.len > dump.len) ? dump.len : tmp.len; | ||
1908 | if (!len) | ||
1909 | return -EFAULT; | ||
1910 | |||
1911 | data = vzalloc(tmp.len); | ||
1912 | if (!data) | ||
1913 | return -ENOMEM; | ||
1914 | ret = ops->get_dump_data(dev, &dump, data); | ||
1915 | if (ret) | ||
1916 | goto out; | ||
1917 | |||
1918 | if (copy_to_user(useraddr, &dump, sizeof(dump))) { | ||
1919 | ret = -EFAULT; | ||
1920 | goto out; | ||
1921 | } | ||
1922 | useraddr += offsetof(struct ethtool_dump, data); | ||
1923 | if (copy_to_user(useraddr, data, len)) | ||
1924 | ret = -EFAULT; | ||
1925 | out: | ||
1926 | vfree(data); | ||
1927 | return ret; | ||
1928 | } | ||
1929 | |||
1392 | /* The main entry point in this file. Called from net/core/dev.c */ | 1930 | /* The main entry point in this file. Called from net/core/dev.c */ |
1393 | 1931 | ||
1394 | int dev_ethtool(struct net *net, struct ifreq *ifr) | 1932 | int dev_ethtool(struct net *net, struct ifreq *ifr) |
@@ -1397,19 +1935,27 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1397 | void __user *useraddr = ifr->ifr_data; | 1935 | void __user *useraddr = ifr->ifr_data; |
1398 | u32 ethcmd; | 1936 | u32 ethcmd; |
1399 | int rc; | 1937 | int rc; |
1400 | unsigned long old_features; | 1938 | u32 old_features; |
1401 | 1939 | ||
1402 | if (!dev || !netif_device_present(dev)) | 1940 | if (!dev || !netif_device_present(dev)) |
1403 | return -ENODEV; | 1941 | return -ENODEV; |
1404 | 1942 | ||
1405 | if (!dev->ethtool_ops) | ||
1406 | return -EOPNOTSUPP; | ||
1407 | |||
1408 | if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) | 1943 | if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) |
1409 | return -EFAULT; | 1944 | return -EFAULT; |
1410 | 1945 | ||
1946 | if (!dev->ethtool_ops) { | ||
1947 | /* ETHTOOL_GDRVINFO does not require any driver support. | ||
1948 | * It is also unprivileged and does not change anything, | ||
1949 | * so we can take a shortcut to it. */ | ||
1950 | if (ethcmd == ETHTOOL_GDRVINFO) | ||
1951 | return ethtool_get_drvinfo(dev, useraddr); | ||
1952 | else | ||
1953 | return -EOPNOTSUPP; | ||
1954 | } | ||
1955 | |||
1411 | /* Allow some commands to be done by anyone */ | 1956 | /* Allow some commands to be done by anyone */ |
1412 | switch (ethcmd) { | 1957 | switch (ethcmd) { |
1958 | case ETHTOOL_GSET: | ||
1413 | case ETHTOOL_GDRVINFO: | 1959 | case ETHTOOL_GDRVINFO: |
1414 | case ETHTOOL_GMSGLVL: | 1960 | case ETHTOOL_GMSGLVL: |
1415 | case ETHTOOL_GCOALESCE: | 1961 | case ETHTOOL_GCOALESCE: |
@@ -1431,6 +1977,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1431 | case ETHTOOL_GRXCLSRLCNT: | 1977 | case ETHTOOL_GRXCLSRLCNT: |
1432 | case ETHTOOL_GRXCLSRULE: | 1978 | case ETHTOOL_GRXCLSRULE: |
1433 | case ETHTOOL_GRXCLSRLALL: | 1979 | case ETHTOOL_GRXCLSRLALL: |
1980 | case ETHTOOL_GFEATURES: | ||
1434 | break; | 1981 | break; |
1435 | default: | 1982 | default: |
1436 | if (!capable(CAP_NET_ADMIN)) | 1983 | if (!capable(CAP_NET_ADMIN)) |
@@ -1475,8 +2022,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1475 | rc = ethtool_nway_reset(dev); | 2022 | rc = ethtool_nway_reset(dev); |
1476 | break; | 2023 | break; |
1477 | case ETHTOOL_GLINK: | 2024 | case ETHTOOL_GLINK: |
1478 | rc = ethtool_get_value(dev, useraddr, ethcmd, | 2025 | rc = ethtool_get_link(dev, useraddr); |
1479 | dev->ethtool_ops->get_link); | ||
1480 | break; | 2026 | break; |
1481 | case ETHTOOL_GEEPROM: | 2027 | case ETHTOOL_GEEPROM: |
1482 | rc = ethtool_get_eeprom(dev, useraddr); | 2028 | rc = ethtool_get_eeprom(dev, useraddr); |
@@ -1502,42 +2048,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1502 | case ETHTOOL_SPAUSEPARAM: | 2048 | case ETHTOOL_SPAUSEPARAM: |
1503 | rc = ethtool_set_pauseparam(dev, useraddr); | 2049 | rc = ethtool_set_pauseparam(dev, useraddr); |
1504 | break; | 2050 | break; |
1505 | case ETHTOOL_GRXCSUM: | ||
1506 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1507 | (dev->ethtool_ops->get_rx_csum ? | ||
1508 | dev->ethtool_ops->get_rx_csum : | ||
1509 | ethtool_op_get_rx_csum)); | ||
1510 | break; | ||
1511 | case ETHTOOL_SRXCSUM: | ||
1512 | rc = ethtool_set_rx_csum(dev, useraddr); | ||
1513 | break; | ||
1514 | case ETHTOOL_GTXCSUM: | ||
1515 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1516 | (dev->ethtool_ops->get_tx_csum ? | ||
1517 | dev->ethtool_ops->get_tx_csum : | ||
1518 | ethtool_op_get_tx_csum)); | ||
1519 | break; | ||
1520 | case ETHTOOL_STXCSUM: | ||
1521 | rc = ethtool_set_tx_csum(dev, useraddr); | ||
1522 | break; | ||
1523 | case ETHTOOL_GSG: | ||
1524 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1525 | (dev->ethtool_ops->get_sg ? | ||
1526 | dev->ethtool_ops->get_sg : | ||
1527 | ethtool_op_get_sg)); | ||
1528 | break; | ||
1529 | case ETHTOOL_SSG: | ||
1530 | rc = ethtool_set_sg(dev, useraddr); | ||
1531 | break; | ||
1532 | case ETHTOOL_GTSO: | ||
1533 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1534 | (dev->ethtool_ops->get_tso ? | ||
1535 | dev->ethtool_ops->get_tso : | ||
1536 | ethtool_op_get_tso)); | ||
1537 | break; | ||
1538 | case ETHTOOL_STSO: | ||
1539 | rc = ethtool_set_tso(dev, useraddr); | ||
1540 | break; | ||
1541 | case ETHTOOL_TEST: | 2051 | case ETHTOOL_TEST: |
1542 | rc = ethtool_self_test(dev, useraddr); | 2052 | rc = ethtool_self_test(dev, useraddr); |
1543 | break; | 2053 | break; |
@@ -1553,21 +2063,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1553 | case ETHTOOL_GPERMADDR: | 2063 | case ETHTOOL_GPERMADDR: |
1554 | rc = ethtool_get_perm_addr(dev, useraddr); | 2064 | rc = ethtool_get_perm_addr(dev, useraddr); |
1555 | break; | 2065 | break; |
1556 | case ETHTOOL_GUFO: | ||
1557 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1558 | (dev->ethtool_ops->get_ufo ? | ||
1559 | dev->ethtool_ops->get_ufo : | ||
1560 | ethtool_op_get_ufo)); | ||
1561 | break; | ||
1562 | case ETHTOOL_SUFO: | ||
1563 | rc = ethtool_set_ufo(dev, useraddr); | ||
1564 | break; | ||
1565 | case ETHTOOL_GGSO: | ||
1566 | rc = ethtool_get_gso(dev, useraddr); | ||
1567 | break; | ||
1568 | case ETHTOOL_SGSO: | ||
1569 | rc = ethtool_set_gso(dev, useraddr); | ||
1570 | break; | ||
1571 | case ETHTOOL_GFLAGS: | 2066 | case ETHTOOL_GFLAGS: |
1572 | rc = ethtool_get_value(dev, useraddr, ethcmd, | 2067 | rc = ethtool_get_value(dev, useraddr, ethcmd, |
1573 | (dev->ethtool_ops->get_flags ? | 2068 | (dev->ethtool_ops->get_flags ? |
@@ -1575,8 +2070,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1575 | ethtool_op_get_flags)); | 2070 | ethtool_op_get_flags)); |
1576 | break; | 2071 | break; |
1577 | case ETHTOOL_SFLAGS: | 2072 | case ETHTOOL_SFLAGS: |
1578 | rc = ethtool_set_value(dev, useraddr, | 2073 | rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags); |
1579 | dev->ethtool_ops->set_flags); | ||
1580 | break; | 2074 | break; |
1581 | case ETHTOOL_GPFLAGS: | 2075 | case ETHTOOL_GPFLAGS: |
1582 | rc = ethtool_get_value(dev, useraddr, ethcmd, | 2076 | rc = ethtool_get_value(dev, useraddr, ethcmd, |
@@ -1598,12 +2092,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1598 | case ETHTOOL_SRXCLSRLINS: | 2092 | case ETHTOOL_SRXCLSRLINS: |
1599 | rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); | 2093 | rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); |
1600 | break; | 2094 | break; |
1601 | case ETHTOOL_GGRO: | ||
1602 | rc = ethtool_get_gro(dev, useraddr); | ||
1603 | break; | ||
1604 | case ETHTOOL_SGRO: | ||
1605 | rc = ethtool_set_gro(dev, useraddr); | ||
1606 | break; | ||
1607 | case ETHTOOL_FLASHDEV: | 2095 | case ETHTOOL_FLASHDEV: |
1608 | rc = ethtool_flash_device(dev, useraddr); | 2096 | rc = ethtool_flash_device(dev, useraddr); |
1609 | break; | 2097 | break; |
@@ -1625,6 +2113,45 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1625 | case ETHTOOL_SRXFHINDIR: | 2113 | case ETHTOOL_SRXFHINDIR: |
1626 | rc = ethtool_set_rxfh_indir(dev, useraddr); | 2114 | rc = ethtool_set_rxfh_indir(dev, useraddr); |
1627 | break; | 2115 | break; |
2116 | case ETHTOOL_GFEATURES: | ||
2117 | rc = ethtool_get_features(dev, useraddr); | ||
2118 | break; | ||
2119 | case ETHTOOL_SFEATURES: | ||
2120 | rc = ethtool_set_features(dev, useraddr); | ||
2121 | break; | ||
2122 | case ETHTOOL_GTXCSUM: | ||
2123 | case ETHTOOL_GRXCSUM: | ||
2124 | case ETHTOOL_GSG: | ||
2125 | case ETHTOOL_GTSO: | ||
2126 | case ETHTOOL_GUFO: | ||
2127 | case ETHTOOL_GGSO: | ||
2128 | case ETHTOOL_GGRO: | ||
2129 | rc = ethtool_get_one_feature(dev, useraddr, ethcmd); | ||
2130 | break; | ||
2131 | case ETHTOOL_STXCSUM: | ||
2132 | case ETHTOOL_SRXCSUM: | ||
2133 | case ETHTOOL_SSG: | ||
2134 | case ETHTOOL_STSO: | ||
2135 | case ETHTOOL_SUFO: | ||
2136 | case ETHTOOL_SGSO: | ||
2137 | case ETHTOOL_SGRO: | ||
2138 | rc = ethtool_set_one_feature(dev, useraddr, ethcmd); | ||
2139 | break; | ||
2140 | case ETHTOOL_GCHANNELS: | ||
2141 | rc = ethtool_get_channels(dev, useraddr); | ||
2142 | break; | ||
2143 | case ETHTOOL_SCHANNELS: | ||
2144 | rc = ethtool_set_channels(dev, useraddr); | ||
2145 | break; | ||
2146 | case ETHTOOL_SET_DUMP: | ||
2147 | rc = ethtool_set_dump(dev, useraddr); | ||
2148 | break; | ||
2149 | case ETHTOOL_GET_DUMP_FLAG: | ||
2150 | rc = ethtool_get_dump_flag(dev, useraddr); | ||
2151 | break; | ||
2152 | case ETHTOOL_GET_DUMP_DATA: | ||
2153 | rc = ethtool_get_dump_data(dev, useraddr); | ||
2154 | break; | ||
1628 | default: | 2155 | default: |
1629 | rc = -EOPNOTSUPP; | 2156 | rc = -EOPNOTSUPP; |
1630 | } | 2157 | } |