diff options
-rw-r--r-- | include/linux/ethtool.h | 85 | ||||
-rw-r--r-- | include/linux/netdevice.h | 37 | ||||
-rw-r--r-- | net/core/dev.c | 46 | ||||
-rw-r--r-- | net/core/ethtool.c | 125 |
4 files changed, 283 insertions, 10 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 1908929204a9..806e716bb4fb 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h | |||
@@ -251,6 +251,7 @@ enum ethtool_stringset { | |||
251 | ETH_SS_STATS, | 251 | ETH_SS_STATS, |
252 | ETH_SS_PRIV_FLAGS, | 252 | ETH_SS_PRIV_FLAGS, |
253 | ETH_SS_NTUPLE_FILTERS, | 253 | ETH_SS_NTUPLE_FILTERS, |
254 | ETH_SS_FEATURES, | ||
254 | }; | 255 | }; |
255 | 256 | ||
256 | /* for passing string sets for data tagging */ | 257 | /* for passing string sets for data tagging */ |
@@ -523,6 +524,87 @@ struct ethtool_flash { | |||
523 | char data[ETHTOOL_FLASH_MAX_FILENAME]; | 524 | char data[ETHTOOL_FLASH_MAX_FILENAME]; |
524 | }; | 525 | }; |
525 | 526 | ||
527 | /* for returning and changing feature sets */ | ||
528 | |||
529 | /** | ||
530 | * struct ethtool_get_features_block - block with state of 32 features | ||
531 | * @available: mask of changeable features | ||
532 | * @requested: mask of features requested to be enabled if possible | ||
533 | * @active: mask of currently enabled features | ||
534 | * @never_changed: mask of features not changeable for any device | ||
535 | */ | ||
536 | struct ethtool_get_features_block { | ||
537 | __u32 available; | ||
538 | __u32 requested; | ||
539 | __u32 active; | ||
540 | __u32 never_changed; | ||
541 | }; | ||
542 | |||
543 | /** | ||
544 | * struct ethtool_gfeatures - command to get state of device's features | ||
545 | * @cmd: command number = %ETHTOOL_GFEATURES | ||
546 | * @size: in: number of elements in the features[] array; | ||
547 | * out: number of elements in features[] needed to hold all features | ||
548 | * @features: state of features | ||
549 | */ | ||
550 | struct ethtool_gfeatures { | ||
551 | __u32 cmd; | ||
552 | __u32 size; | ||
553 | struct ethtool_get_features_block features[0]; | ||
554 | }; | ||
555 | |||
556 | /** | ||
557 | * struct ethtool_set_features_block - block with request for 32 features | ||
558 | * @valid: mask of features to be changed | ||
559 | * @requested: values of features to be changed | ||
560 | */ | ||
561 | struct ethtool_set_features_block { | ||
562 | __u32 valid; | ||
563 | __u32 requested; | ||
564 | }; | ||
565 | |||
566 | /** | ||
567 | * struct ethtool_sfeatures - command to request change in device's features | ||
568 | * @cmd: command number = %ETHTOOL_SFEATURES | ||
569 | * @size: array size of the features[] array | ||
570 | * @features: feature change masks | ||
571 | */ | ||
572 | struct ethtool_sfeatures { | ||
573 | __u32 cmd; | ||
574 | __u32 size; | ||
575 | struct ethtool_set_features_block features[0]; | ||
576 | }; | ||
577 | |||
578 | /* | ||
579 | * %ETHTOOL_SFEATURES changes features present in features[].valid to the | ||
580 | * values of corresponding bits in features[].requested. Bits in .requested | ||
581 | * not set in .valid or not changeable are ignored. | ||
582 | * | ||
583 | * Returns %EINVAL when .valid contains undefined or never-changable bits | ||
584 | * or size is not equal to required number of features words (32-bit blocks). | ||
585 | * Returns >= 0 if request was completed; bits set in the value mean: | ||
586 | * %ETHTOOL_F_UNSUPPORTED - there were bits set in .valid that are not | ||
587 | * changeable (not present in %ETHTOOL_GFEATURES' features[].available) | ||
588 | * those bits were ignored. | ||
589 | * %ETHTOOL_F_WISH - some or all changes requested were recorded but the | ||
590 | * resulting state of bits masked by .valid is not equal to .requested. | ||
591 | * Probably there are other device-specific constraints on some features | ||
592 | * in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered | ||
593 | * here as though ignored bits were cleared. | ||
594 | * | ||
595 | * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of | ||
596 | * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands | ||
597 | * for ETH_SS_FEATURES string set. First entry in the table corresponds to least | ||
598 | * significant bit in features[0] fields. Empty strings mark undefined features. | ||
599 | */ | ||
600 | enum ethtool_sfeatures_retval_bits { | ||
601 | ETHTOOL_F_UNSUPPORTED__BIT, | ||
602 | ETHTOOL_F_WISH__BIT, | ||
603 | }; | ||
604 | |||
605 | #define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT) | ||
606 | #define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT) | ||
607 | |||
526 | #ifdef __KERNEL__ | 608 | #ifdef __KERNEL__ |
527 | 609 | ||
528 | #include <linux/rculist.h> | 610 | #include <linux/rculist.h> |
@@ -744,6 +826,9 @@ struct ethtool_ops { | |||
744 | #define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */ | 826 | #define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */ |
745 | #define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */ | 827 | #define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */ |
746 | 828 | ||
829 | #define ETHTOOL_GFEATURES 0x0000003a /* Get device offload settings */ | ||
830 | #define ETHTOOL_SFEATURES 0x0000003b /* Change device offload settings */ | ||
831 | |||
747 | /* compatibility with older code */ | 832 | /* compatibility with older code */ |
748 | #define SPARC_ETH_GSET ETHTOOL_GSET | 833 | #define SPARC_ETH_GSET ETHTOOL_GSET |
749 | #define SPARC_ETH_SSET ETHTOOL_SSET | 834 | #define SPARC_ETH_SSET ETHTOOL_SSET |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index dede3fdbb4be..85f67e225f60 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -791,6 +791,18 @@ struct netdev_tc_txq { | |||
791 | * | 791 | * |
792 | * int (*ndo_del_slave)(struct net_device *dev, struct net_device *slave_dev); | 792 | * int (*ndo_del_slave)(struct net_device *dev, struct net_device *slave_dev); |
793 | * Called to release previously enslaved netdev. | 793 | * Called to release previously enslaved netdev. |
794 | * | ||
795 | * Feature/offload setting functions. | ||
796 | * u32 (*ndo_fix_features)(struct net_device *dev, u32 features); | ||
797 | * Adjusts the requested feature flags according to device-specific | ||
798 | * constraints, and returns the resulting flags. Must not modify | ||
799 | * the device state. | ||
800 | * | ||
801 | * int (*ndo_set_features)(struct net_device *dev, u32 features); | ||
802 | * Called to update device configuration to new features. Passed | ||
803 | * feature set might be less than what was returned by ndo_fix_features()). | ||
804 | * Must return >0 or -errno if it changed dev->features itself. | ||
805 | * | ||
794 | */ | 806 | */ |
795 | #define HAVE_NET_DEVICE_OPS | 807 | #define HAVE_NET_DEVICE_OPS |
796 | struct net_device_ops { | 808 | struct net_device_ops { |
@@ -874,6 +886,10 @@ struct net_device_ops { | |||
874 | struct net_device *slave_dev); | 886 | struct net_device *slave_dev); |
875 | int (*ndo_del_slave)(struct net_device *dev, | 887 | int (*ndo_del_slave)(struct net_device *dev, |
876 | struct net_device *slave_dev); | 888 | struct net_device *slave_dev); |
889 | u32 (*ndo_fix_features)(struct net_device *dev, | ||
890 | u32 features); | ||
891 | int (*ndo_set_features)(struct net_device *dev, | ||
892 | u32 features); | ||
877 | }; | 893 | }; |
878 | 894 | ||
879 | /* | 895 | /* |
@@ -925,12 +941,18 @@ struct net_device { | |||
925 | struct list_head napi_list; | 941 | struct list_head napi_list; |
926 | struct list_head unreg_list; | 942 | struct list_head unreg_list; |
927 | 943 | ||
928 | /* Net device features */ | 944 | /* currently active device features */ |
929 | u32 features; | 945 | u32 features; |
930 | 946 | /* user-changeable features */ | |
947 | u32 hw_features; | ||
948 | /* user-requested features */ | ||
949 | u32 wanted_features; | ||
931 | /* VLAN feature mask */ | 950 | /* VLAN feature mask */ |
932 | u32 vlan_features; | 951 | u32 vlan_features; |
933 | 952 | ||
953 | /* Net device feature bits; if you change something, | ||
954 | * also update netdev_features_strings[] in ethtool.c */ | ||
955 | |||
934 | #define NETIF_F_SG 1 /* Scatter/gather IO. */ | 956 | #define NETIF_F_SG 1 /* Scatter/gather IO. */ |
935 | #define NETIF_F_IP_CSUM 2 /* Can checksum TCP/UDP over IPv4. */ | 957 | #define NETIF_F_IP_CSUM 2 /* Can checksum TCP/UDP over IPv4. */ |
936 | #define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */ | 958 | #define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */ |
@@ -966,6 +988,12 @@ struct net_device { | |||
966 | #define NETIF_F_TSO6 (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT) | 988 | #define NETIF_F_TSO6 (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT) |
967 | #define NETIF_F_FSO (SKB_GSO_FCOE << NETIF_F_GSO_SHIFT) | 989 | #define NETIF_F_FSO (SKB_GSO_FCOE << NETIF_F_GSO_SHIFT) |
968 | 990 | ||
991 | /* Features valid for ethtool to change */ | ||
992 | /* = all defined minus driver/device-class-related */ | ||
993 | #define NETIF_F_NEVER_CHANGE (NETIF_F_HIGHDMA | NETIF_F_VLAN_CHALLENGED | \ | ||
994 | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL) | ||
995 | #define NETIF_F_ETHTOOL_BITS (0x1f3fffff & ~NETIF_F_NEVER_CHANGE) | ||
996 | |||
969 | /* List of features with software fallbacks. */ | 997 | /* List of features with software fallbacks. */ |
970 | #define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \ | 998 | #define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \ |
971 | NETIF_F_TSO6 | NETIF_F_UFO) | 999 | NETIF_F_TSO6 | NETIF_F_UFO) |
@@ -2428,8 +2456,13 @@ extern char *netdev_drivername(const struct net_device *dev, char *buffer, int l | |||
2428 | 2456 | ||
2429 | extern void linkwatch_run_queue(void); | 2457 | extern void linkwatch_run_queue(void); |
2430 | 2458 | ||
2459 | static inline u32 netdev_get_wanted_features(struct net_device *dev) | ||
2460 | { | ||
2461 | return (dev->features & ~dev->hw_features) | dev->wanted_features; | ||
2462 | } | ||
2431 | u32 netdev_increment_features(u32 all, u32 one, u32 mask); | 2463 | u32 netdev_increment_features(u32 all, u32 one, u32 mask); |
2432 | u32 netdev_fix_features(struct net_device *dev, u32 features); | 2464 | u32 netdev_fix_features(struct net_device *dev, u32 features); |
2465 | void netdev_update_features(struct net_device *dev); | ||
2433 | 2466 | ||
2434 | void netif_stacked_transfer_operstate(const struct net_device *rootdev, | 2467 | void netif_stacked_transfer_operstate(const struct net_device *rootdev, |
2435 | struct net_device *dev); | 2468 | struct net_device *dev); |
diff --git a/net/core/dev.c b/net/core/dev.c index 8686f6ffe7f0..4f6943928fe8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -5302,6 +5302,37 @@ u32 netdev_fix_features(struct net_device *dev, u32 features) | |||
5302 | } | 5302 | } |
5303 | EXPORT_SYMBOL(netdev_fix_features); | 5303 | EXPORT_SYMBOL(netdev_fix_features); |
5304 | 5304 | ||
5305 | void netdev_update_features(struct net_device *dev) | ||
5306 | { | ||
5307 | u32 features; | ||
5308 | int err = 0; | ||
5309 | |||
5310 | features = netdev_get_wanted_features(dev); | ||
5311 | |||
5312 | if (dev->netdev_ops->ndo_fix_features) | ||
5313 | features = dev->netdev_ops->ndo_fix_features(dev, features); | ||
5314 | |||
5315 | /* driver might be less strict about feature dependencies */ | ||
5316 | features = netdev_fix_features(dev, features); | ||
5317 | |||
5318 | if (dev->features == features) | ||
5319 | return; | ||
5320 | |||
5321 | netdev_info(dev, "Features changed: 0x%08x -> 0x%08x\n", | ||
5322 | dev->features, features); | ||
5323 | |||
5324 | if (dev->netdev_ops->ndo_set_features) | ||
5325 | err = dev->netdev_ops->ndo_set_features(dev, features); | ||
5326 | |||
5327 | if (!err) | ||
5328 | dev->features = features; | ||
5329 | else if (err < 0) | ||
5330 | netdev_err(dev, | ||
5331 | "set_features() failed (%d); wanted 0x%08x, left 0x%08x\n", | ||
5332 | err, features, dev->features); | ||
5333 | } | ||
5334 | EXPORT_SYMBOL(netdev_update_features); | ||
5335 | |||
5305 | /** | 5336 | /** |
5306 | * netif_stacked_transfer_operstate - transfer operstate | 5337 | * netif_stacked_transfer_operstate - transfer operstate |
5307 | * @rootdev: the root or lower level device to transfer state from | 5338 | * @rootdev: the root or lower level device to transfer state from |
@@ -5436,15 +5467,18 @@ int register_netdevice(struct net_device *dev) | |||
5436 | if (dev->iflink == -1) | 5467 | if (dev->iflink == -1) |
5437 | dev->iflink = dev->ifindex; | 5468 | dev->iflink = dev->ifindex; |
5438 | 5469 | ||
5439 | /* Enable software offloads by default - will be stripped in | 5470 | /* Transfer changeable features to wanted_features and enable |
5440 | * netdev_fix_features() if not supported. */ | 5471 | * software offloads (GSO and GRO). |
5441 | dev->features |= NETIF_F_SOFT_FEATURES; | 5472 | */ |
5473 | dev->hw_features |= NETIF_F_SOFT_FEATURES; | ||
5474 | dev->wanted_features = (dev->features & dev->hw_features) | ||
5475 | | NETIF_F_SOFT_FEATURES; | ||
5442 | 5476 | ||
5443 | /* Avoid warning from netdev_fix_features() for GSO without SG */ | 5477 | /* Avoid warning from netdev_fix_features() for GSO without SG */ |
5444 | if (!(dev->features & NETIF_F_SG)) | 5478 | if (!(dev->wanted_features & NETIF_F_SG)) |
5445 | dev->features &= ~NETIF_F_GSO; | 5479 | dev->wanted_features &= ~NETIF_F_GSO; |
5446 | 5480 | ||
5447 | dev->features = netdev_fix_features(dev, dev->features); | 5481 | netdev_update_features(dev); |
5448 | 5482 | ||
5449 | /* Enable GRO and NETIF_F_HIGHDMA for vlans by default, | 5483 | /* Enable GRO and NETIF_F_HIGHDMA for vlans by default, |
5450 | * vlan_dev_init() will do the dev->features check, so these features | 5484 | * vlan_dev_init() will do the dev->features check, so these features |
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index c3fb8f90de6d..95773960dc77 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -172,10 +172,120 @@ EXPORT_SYMBOL(ethtool_ntuple_flush); | |||
172 | 172 | ||
173 | /* Handlers for each ethtool command */ | 173 | /* Handlers for each ethtool command */ |
174 | 174 | ||
175 | #define ETHTOOL_DEV_FEATURE_WORDS 1 | ||
176 | |||
177 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) | ||
178 | { | ||
179 | struct ethtool_gfeatures cmd = { | ||
180 | .cmd = ETHTOOL_GFEATURES, | ||
181 | .size = ETHTOOL_DEV_FEATURE_WORDS, | ||
182 | }; | ||
183 | struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS] = { | ||
184 | { | ||
185 | .available = dev->hw_features, | ||
186 | .requested = dev->wanted_features, | ||
187 | .active = dev->features, | ||
188 | .never_changed = NETIF_F_NEVER_CHANGE, | ||
189 | }, | ||
190 | }; | ||
191 | u32 __user *sizeaddr; | ||
192 | u32 copy_size; | ||
193 | |||
194 | sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size); | ||
195 | if (get_user(copy_size, sizeaddr)) | ||
196 | return -EFAULT; | ||
197 | |||
198 | if (copy_size > ETHTOOL_DEV_FEATURE_WORDS) | ||
199 | copy_size = ETHTOOL_DEV_FEATURE_WORDS; | ||
200 | |||
201 | if (copy_to_user(useraddr, &cmd, sizeof(cmd))) | ||
202 | return -EFAULT; | ||
203 | useraddr += sizeof(cmd); | ||
204 | if (copy_to_user(useraddr, features, copy_size * sizeof(*features))) | ||
205 | return -EFAULT; | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static int ethtool_set_features(struct net_device *dev, void __user *useraddr) | ||
211 | { | ||
212 | struct ethtool_sfeatures cmd; | ||
213 | struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; | ||
214 | int ret = 0; | ||
215 | |||
216 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) | ||
217 | return -EFAULT; | ||
218 | useraddr += sizeof(cmd); | ||
219 | |||
220 | if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS) | ||
221 | return -EINVAL; | ||
222 | |||
223 | if (copy_from_user(features, useraddr, sizeof(features))) | ||
224 | return -EFAULT; | ||
225 | |||
226 | if (features[0].valid & ~NETIF_F_ETHTOOL_BITS) | ||
227 | return -EINVAL; | ||
228 | |||
229 | if (features[0].valid & ~dev->hw_features) { | ||
230 | features[0].valid &= dev->hw_features; | ||
231 | ret |= ETHTOOL_F_UNSUPPORTED; | ||
232 | } | ||
233 | |||
234 | dev->wanted_features &= ~features[0].valid; | ||
235 | dev->wanted_features |= features[0].valid & features[0].requested; | ||
236 | netdev_update_features(dev); | ||
237 | |||
238 | if ((dev->wanted_features ^ dev->features) & features[0].valid) | ||
239 | ret |= ETHTOOL_F_WISH; | ||
240 | |||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GSTRING_LEN] = { | ||
245 | /* NETIF_F_SG */ "tx-scatter-gather", | ||
246 | /* NETIF_F_IP_CSUM */ "tx-checksum-ipv4", | ||
247 | /* NETIF_F_NO_CSUM */ "tx-checksum-unneeded", | ||
248 | /* NETIF_F_HW_CSUM */ "tx-checksum-ip-generic", | ||
249 | /* NETIF_F_IPV6_CSUM */ "tx_checksum-ipv6", | ||
250 | /* NETIF_F_HIGHDMA */ "highdma", | ||
251 | /* NETIF_F_FRAGLIST */ "tx-scatter-gather-fraglist", | ||
252 | /* NETIF_F_HW_VLAN_TX */ "tx-vlan-hw-insert", | ||
253 | |||
254 | /* NETIF_F_HW_VLAN_RX */ "rx-vlan-hw-parse", | ||
255 | /* NETIF_F_HW_VLAN_FILTER */ "rx-vlan-filter", | ||
256 | /* NETIF_F_VLAN_CHALLENGED */ "vlan-challenged", | ||
257 | /* NETIF_F_GSO */ "tx-generic-segmentation", | ||
258 | /* NETIF_F_LLTX */ "tx-lockless", | ||
259 | /* NETIF_F_NETNS_LOCAL */ "netns-local", | ||
260 | /* NETIF_F_GRO */ "rx-gro", | ||
261 | /* NETIF_F_LRO */ "rx-lro", | ||
262 | |||
263 | /* NETIF_F_TSO */ "tx-tcp-segmentation", | ||
264 | /* NETIF_F_UFO */ "tx-udp-fragmentation", | ||
265 | /* NETIF_F_GSO_ROBUST */ "tx-gso-robust", | ||
266 | /* NETIF_F_TSO_ECN */ "tx-tcp-ecn-segmentation", | ||
267 | /* NETIF_F_TSO6 */ "tx-tcp6-segmentation", | ||
268 | /* NETIF_F_FSO */ "tx-fcoe-segmentation", | ||
269 | "", | ||
270 | "", | ||
271 | |||
272 | /* NETIF_F_FCOE_CRC */ "tx-checksum-fcoe-crc", | ||
273 | /* NETIF_F_SCTP_CSUM */ "tx-checksum-sctp", | ||
274 | /* NETIF_F_FCOE_MTU */ "fcoe-mtu", | ||
275 | /* NETIF_F_NTUPLE */ "rx-ntuple-filter", | ||
276 | /* NETIF_F_RXHASH */ "rx-hashing", | ||
277 | "", | ||
278 | "", | ||
279 | "", | ||
280 | }; | ||
281 | |||
175 | static int __ethtool_get_sset_count(struct net_device *dev, int sset) | 282 | static int __ethtool_get_sset_count(struct net_device *dev, int sset) |
176 | { | 283 | { |
177 | const struct ethtool_ops *ops = dev->ethtool_ops; | 284 | const struct ethtool_ops *ops = dev->ethtool_ops; |
178 | 285 | ||
286 | if (sset == ETH_SS_FEATURES) | ||
287 | return ARRAY_SIZE(netdev_features_strings); | ||
288 | |||
179 | if (ops && ops->get_sset_count && ops->get_strings) | 289 | if (ops && ops->get_sset_count && ops->get_strings) |
180 | return ops->get_sset_count(dev, sset); | 290 | return ops->get_sset_count(dev, sset); |
181 | else | 291 | else |
@@ -187,8 +297,12 @@ static void __ethtool_get_strings(struct net_device *dev, | |||
187 | { | 297 | { |
188 | const struct ethtool_ops *ops = dev->ethtool_ops; | 298 | const struct ethtool_ops *ops = dev->ethtool_ops; |
189 | 299 | ||
190 | /* ops->get_strings is valid because checked earlier */ | 300 | if (stringset == ETH_SS_FEATURES) |
191 | ops->get_strings(dev, stringset, data); | 301 | memcpy(data, netdev_features_strings, |
302 | sizeof(netdev_features_strings)); | ||
303 | else | ||
304 | /* ops->get_strings is valid because checked earlier */ | ||
305 | ops->get_strings(dev, stringset, data); | ||
192 | } | 306 | } |
193 | 307 | ||
194 | static u32 ethtool_get_feature_mask(u32 eth_cmd) | 308 | static u32 ethtool_get_feature_mask(u32 eth_cmd) |
@@ -1533,6 +1647,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1533 | case ETHTOOL_GRXCLSRLCNT: | 1647 | case ETHTOOL_GRXCLSRLCNT: |
1534 | case ETHTOOL_GRXCLSRULE: | 1648 | case ETHTOOL_GRXCLSRULE: |
1535 | case ETHTOOL_GRXCLSRLALL: | 1649 | case ETHTOOL_GRXCLSRLALL: |
1650 | case ETHTOOL_GFEATURES: | ||
1536 | break; | 1651 | break; |
1537 | default: | 1652 | default: |
1538 | if (!capable(CAP_NET_ADMIN)) | 1653 | if (!capable(CAP_NET_ADMIN)) |
@@ -1678,6 +1793,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1678 | case ETHTOOL_SRXFHINDIR: | 1793 | case ETHTOOL_SRXFHINDIR: |
1679 | rc = ethtool_set_rxfh_indir(dev, useraddr); | 1794 | rc = ethtool_set_rxfh_indir(dev, useraddr); |
1680 | break; | 1795 | break; |
1796 | case ETHTOOL_GFEATURES: | ||
1797 | rc = ethtool_get_features(dev, useraddr); | ||
1798 | break; | ||
1799 | case ETHTOOL_SFEATURES: | ||
1800 | rc = ethtool_set_features(dev, useraddr); | ||
1801 | break; | ||
1681 | case ETHTOOL_GTXCSUM: | 1802 | case ETHTOOL_GTXCSUM: |
1682 | case ETHTOOL_GSG: | 1803 | case ETHTOOL_GSG: |
1683 | case ETHTOOL_GTSO: | 1804 | case ETHTOOL_GTSO: |