diff options
-rw-r--r-- | include/linux/ethtool.h | 7 | ||||
-rw-r--r-- | net/core/ethtool.c | 95 |
2 files changed, 75 insertions, 27 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 0e5de2fb960c..f617998b87f1 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h | |||
@@ -376,11 +376,9 @@ struct ethtool_ops { | |||
376 | int (*set_sg)(struct net_device *, u32); | 376 | int (*set_sg)(struct net_device *, u32); |
377 | u32 (*get_tso)(struct net_device *); | 377 | u32 (*get_tso)(struct net_device *); |
378 | int (*set_tso)(struct net_device *, u32); | 378 | int (*set_tso)(struct net_device *, u32); |
379 | int (*self_test_count)(struct net_device *); | ||
380 | void (*self_test)(struct net_device *, struct ethtool_test *, u64 *); | 379 | void (*self_test)(struct net_device *, struct ethtool_test *, u64 *); |
381 | void (*get_strings)(struct net_device *, u32 stringset, u8 *); | 380 | void (*get_strings)(struct net_device *, u32 stringset, u8 *); |
382 | int (*phys_id)(struct net_device *, u32); | 381 | int (*phys_id)(struct net_device *, u32); |
383 | int (*get_stats_count)(struct net_device *); | ||
384 | void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *); | 382 | void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *); |
385 | int (*begin)(struct net_device *); | 383 | int (*begin)(struct net_device *); |
386 | void (*complete)(struct net_device *); | 384 | void (*complete)(struct net_device *); |
@@ -388,6 +386,11 @@ struct ethtool_ops { | |||
388 | int (*set_ufo)(struct net_device *, u32); | 386 | int (*set_ufo)(struct net_device *, u32); |
389 | u32 (*get_flags)(struct net_device *); | 387 | u32 (*get_flags)(struct net_device *); |
390 | int (*set_flags)(struct net_device *, u32); | 388 | int (*set_flags)(struct net_device *, u32); |
389 | int (*get_sset_count)(struct net_device *, int); | ||
390 | |||
391 | /* the following hooks are obsolete */ | ||
392 | int (*self_test_count)(struct net_device *);/* use get_sset_count */ | ||
393 | int (*get_stats_count)(struct net_device *);/* use get_sset_count */ | ||
391 | }; | 394 | }; |
392 | #endif /* __KERNEL__ */ | 395 | #endif /* __KERNEL__ */ |
393 | 396 | ||
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 6f8b78bcfd84..1edae460d616 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -179,10 +179,23 @@ static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr) | |||
179 | info.cmd = ETHTOOL_GDRVINFO; | 179 | info.cmd = ETHTOOL_GDRVINFO; |
180 | ops->get_drvinfo(dev, &info); | 180 | ops->get_drvinfo(dev, &info); |
181 | 181 | ||
182 | if (ops->self_test_count) | 182 | if (ops->get_sset_count) { |
183 | info.testinfo_len = ops->self_test_count(dev); | 183 | int rc; |
184 | if (ops->get_stats_count) | 184 | |
185 | info.n_stats = ops->get_stats_count(dev); | 185 | rc = ops->get_sset_count(dev, ETH_SS_TEST); |
186 | if (rc >= 0) | ||
187 | info.testinfo_len = rc; | ||
188 | rc = ops->get_sset_count(dev, ETH_SS_STATS); | ||
189 | if (rc >= 0) | ||
190 | info.n_stats = rc; | ||
191 | } else { | ||
192 | /* code path for obsolete hooks */ | ||
193 | |||
194 | if (ops->self_test_count) | ||
195 | info.testinfo_len = ops->self_test_count(dev); | ||
196 | if (ops->get_stats_count) | ||
197 | info.n_stats = ops->get_stats_count(dev); | ||
198 | } | ||
186 | if (ops->get_regs_len) | 199 | if (ops->get_regs_len) |
187 | info.regdump_len = ops->get_regs_len(dev); | 200 | info.regdump_len = ops->get_regs_len(dev); |
188 | if (ops->get_eeprom_len) | 201 | if (ops->get_eeprom_len) |
@@ -669,16 +682,27 @@ static int ethtool_self_test(struct net_device *dev, char __user *useraddr) | |||
669 | struct ethtool_test test; | 682 | struct ethtool_test test; |
670 | const struct ethtool_ops *ops = dev->ethtool_ops; | 683 | const struct ethtool_ops *ops = dev->ethtool_ops; |
671 | u64 *data; | 684 | u64 *data; |
672 | int ret; | 685 | int ret, test_len; |
673 | 686 | ||
674 | if (!ops->self_test || !ops->self_test_count) | 687 | if (!ops->self_test) |
688 | return -EOPNOTSUPP; | ||
689 | if (!ops->get_sset_count && !ops->self_test_count) | ||
675 | return -EOPNOTSUPP; | 690 | return -EOPNOTSUPP; |
676 | 691 | ||
692 | if (ops->get_sset_count) | ||
693 | test_len = ops->get_sset_count(dev, ETH_SS_TEST); | ||
694 | else | ||
695 | /* code path for obsolete hook */ | ||
696 | test_len = ops->self_test_count(dev); | ||
697 | if (test_len < 0) | ||
698 | return test_len; | ||
699 | WARN_ON(test_len == 0); | ||
700 | |||
677 | if (copy_from_user(&test, useraddr, sizeof(test))) | 701 | if (copy_from_user(&test, useraddr, sizeof(test))) |
678 | return -EFAULT; | 702 | return -EFAULT; |
679 | 703 | ||
680 | test.len = ops->self_test_count(dev); | 704 | test.len = test_len; |
681 | data = kmalloc(test.len * sizeof(u64), GFP_USER); | 705 | data = kmalloc(test_len * sizeof(u64), GFP_USER); |
682 | if (!data) | 706 | if (!data) |
683 | return -ENOMEM; | 707 | return -ENOMEM; |
684 | 708 | ||
@@ -710,19 +734,29 @@ static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) | |||
710 | if (copy_from_user(&gstrings, useraddr, sizeof(gstrings))) | 734 | if (copy_from_user(&gstrings, useraddr, sizeof(gstrings))) |
711 | return -EFAULT; | 735 | return -EFAULT; |
712 | 736 | ||
713 | switch (gstrings.string_set) { | 737 | if (ops->get_sset_count) { |
714 | case ETH_SS_TEST: | 738 | ret = ops->get_sset_count(dev, gstrings.string_set); |
715 | if (!ops->self_test_count) | 739 | if (ret < 0) |
716 | return -EOPNOTSUPP; | 740 | return ret; |
717 | gstrings.len = ops->self_test_count(dev); | 741 | |
718 | break; | 742 | gstrings.len = ret; |
719 | case ETH_SS_STATS: | 743 | } else { |
720 | if (!ops->get_stats_count) | 744 | /* code path for obsolete hooks */ |
721 | return -EOPNOTSUPP; | 745 | |
722 | gstrings.len = ops->get_stats_count(dev); | 746 | switch (gstrings.string_set) { |
723 | break; | 747 | case ETH_SS_TEST: |
724 | default: | 748 | if (!ops->self_test_count) |
725 | return -EINVAL; | 749 | return -EOPNOTSUPP; |
750 | gstrings.len = ops->self_test_count(dev); | ||
751 | break; | ||
752 | case ETH_SS_STATS: | ||
753 | if (!ops->get_stats_count) | ||
754 | return -EOPNOTSUPP; | ||
755 | gstrings.len = ops->get_stats_count(dev); | ||
756 | break; | ||
757 | default: | ||
758 | return -EINVAL; | ||
759 | } | ||
726 | } | 760 | } |
727 | 761 | ||
728 | data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER); | 762 | data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER); |
@@ -762,16 +796,27 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) | |||
762 | struct ethtool_stats stats; | 796 | struct ethtool_stats stats; |
763 | const struct ethtool_ops *ops = dev->ethtool_ops; | 797 | const struct ethtool_ops *ops = dev->ethtool_ops; |
764 | u64 *data; | 798 | u64 *data; |
765 | int ret; | 799 | int ret, n_stats; |
766 | 800 | ||
767 | if (!ops->get_ethtool_stats || !ops->get_stats_count) | 801 | if (!ops->get_ethtool_stats) |
768 | return -EOPNOTSUPP; | 802 | return -EOPNOTSUPP; |
803 | if (!ops->get_sset_count && !ops->get_stats_count) | ||
804 | return -EOPNOTSUPP; | ||
805 | |||
806 | if (ops->get_sset_count) | ||
807 | n_stats = ops->get_sset_count(dev, ETH_SS_STATS); | ||
808 | else | ||
809 | /* code path for obsolete hook */ | ||
810 | n_stats = ops->get_stats_count(dev); | ||
811 | if (n_stats < 0) | ||
812 | return n_stats; | ||
813 | WARN_ON(n_stats == 0); | ||
769 | 814 | ||
770 | if (copy_from_user(&stats, useraddr, sizeof(stats))) | 815 | if (copy_from_user(&stats, useraddr, sizeof(stats))) |
771 | return -EFAULT; | 816 | return -EFAULT; |
772 | 817 | ||
773 | stats.n_stats = ops->get_stats_count(dev); | 818 | stats.n_stats = n_stats; |
774 | data = kmalloc(stats.n_stats * sizeof(u64), GFP_USER); | 819 | data = kmalloc(n_stats * sizeof(u64), GFP_USER); |
775 | if (!data) | 820 | if (!data) |
776 | return -ENOMEM; | 821 | return -ENOMEM; |
777 | 822 | ||