diff options
-rw-r--r-- | include/linux/ethtool.h | 5 | ||||
-rw-r--r-- | net/core/ethtool.c | 61 |
2 files changed, 66 insertions, 0 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 54d776c2c1b5..aac3e2eeb4fd 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h | |||
@@ -591,6 +591,9 @@ struct ethtool_sfeatures { | |||
591 | * Probably there are other device-specific constraints on some features | 591 | * Probably there are other device-specific constraints on some features |
592 | * in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered | 592 | * in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered |
593 | * here as though ignored bits were cleared. | 593 | * here as though ignored bits were cleared. |
594 | * %ETHTOOL_F_COMPAT - some or all changes requested were made by calling | ||
595 | * compatibility functions. Requested offload state cannot be properly | ||
596 | * managed by kernel. | ||
594 | * | 597 | * |
595 | * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of | 598 | * 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 | 599 | * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands |
@@ -600,10 +603,12 @@ struct ethtool_sfeatures { | |||
600 | enum ethtool_sfeatures_retval_bits { | 603 | enum ethtool_sfeatures_retval_bits { |
601 | ETHTOOL_F_UNSUPPORTED__BIT, | 604 | ETHTOOL_F_UNSUPPORTED__BIT, |
602 | ETHTOOL_F_WISH__BIT, | 605 | ETHTOOL_F_WISH__BIT, |
606 | ETHTOOL_F_COMPAT__BIT, | ||
603 | }; | 607 | }; |
604 | 608 | ||
605 | #define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT) | 609 | #define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT) |
606 | #define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT) | 610 | #define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT) |
611 | #define ETHTOOL_F_COMPAT (1 << ETHTOOL_F_COMPAT__BIT) | ||
607 | 612 | ||
608 | #ifdef __KERNEL__ | 613 | #ifdef __KERNEL__ |
609 | 614 | ||
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 69a3edc182f9..c1a71bb738da 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -178,6 +178,64 @@ static void ethtool_get_features_compat(struct net_device *dev, | |||
178 | if (dev->ethtool_ops->get_rx_csum) | 178 | if (dev->ethtool_ops->get_rx_csum) |
179 | if (dev->ethtool_ops->get_rx_csum(dev)) | 179 | if (dev->ethtool_ops->get_rx_csum(dev)) |
180 | features[0].active |= NETIF_F_RXCSUM; | 180 | features[0].active |= NETIF_F_RXCSUM; |
181 | |||
182 | /* mark legacy-changeable features */ | ||
183 | if (dev->ethtool_ops->set_sg) | ||
184 | features[0].available |= NETIF_F_SG; | ||
185 | if (dev->ethtool_ops->set_tx_csum) | ||
186 | features[0].available |= NETIF_F_ALL_CSUM; | ||
187 | if (dev->ethtool_ops->set_tso) | ||
188 | features[0].available |= NETIF_F_ALL_TSO; | ||
189 | if (dev->ethtool_ops->set_rx_csum) | ||
190 | features[0].available |= NETIF_F_RXCSUM; | ||
191 | if (dev->ethtool_ops->set_flags) | ||
192 | features[0].available |= flags_dup_features; | ||
193 | } | ||
194 | |||
195 | static int ethtool_set_feature_compat(struct net_device *dev, | ||
196 | int (*legacy_set)(struct net_device *, u32), | ||
197 | struct ethtool_set_features_block *features, u32 mask) | ||
198 | { | ||
199 | u32 do_set; | ||
200 | |||
201 | if (!legacy_set) | ||
202 | return 0; | ||
203 | |||
204 | if (!(features[0].valid & mask)) | ||
205 | return 0; | ||
206 | |||
207 | features[0].valid &= ~mask; | ||
208 | |||
209 | do_set = !!(features[0].requested & mask); | ||
210 | |||
211 | if (legacy_set(dev, do_set) < 0) | ||
212 | netdev_info(dev, | ||
213 | "Legacy feature change (%s) failed for 0x%08x\n", | ||
214 | do_set ? "set" : "clear", mask); | ||
215 | |||
216 | return 1; | ||
217 | } | ||
218 | |||
219 | static int ethtool_set_features_compat(struct net_device *dev, | ||
220 | struct ethtool_set_features_block *features) | ||
221 | { | ||
222 | int compat; | ||
223 | |||
224 | if (!dev->ethtool_ops) | ||
225 | return 0; | ||
226 | |||
227 | compat = ethtool_set_feature_compat(dev, dev->ethtool_ops->set_sg, | ||
228 | features, NETIF_F_SG); | ||
229 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tx_csum, | ||
230 | features, NETIF_F_ALL_CSUM); | ||
231 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tso, | ||
232 | features, NETIF_F_ALL_TSO); | ||
233 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_rx_csum, | ||
234 | features, NETIF_F_RXCSUM); | ||
235 | compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_flags, | ||
236 | features, flags_dup_features); | ||
237 | |||
238 | return compat; | ||
181 | } | 239 | } |
182 | 240 | ||
183 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) | 241 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) |
@@ -234,6 +292,9 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr) | |||
234 | if (features[0].valid & ~NETIF_F_ETHTOOL_BITS) | 292 | if (features[0].valid & ~NETIF_F_ETHTOOL_BITS) |
235 | return -EINVAL; | 293 | return -EINVAL; |
236 | 294 | ||
295 | if (ethtool_set_features_compat(dev, features)) | ||
296 | ret |= ETHTOOL_F_COMPAT; | ||
297 | |||
237 | if (features[0].valid & ~dev->hw_features) { | 298 | if (features[0].valid & ~dev->hw_features) { |
238 | features[0].valid &= dev->hw_features; | 299 | features[0].valid &= dev->hw_features; |
239 | ret |= ETHTOOL_F_UNSUPPORTED; | 300 | ret |= ETHTOOL_F_UNSUPPORTED; |