diff options
author | Michał Mirosław <mirq-linux@rere.qmqm.pl> | 2011-02-15 11:59:17 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-02-17 17:16:33 -0500 |
commit | 5455c6998d34dc983a8693500e4dffefc3682dc5 (patch) | |
tree | b765aecf6d33d8c550cde78368ccc8654951ec07 /net/core | |
parent | 0a417704777ed29d0e8c72b7274a328e61248e75 (diff) |
net: Introduce new feature setting ops
This introduces a new framework to handle device features setting.
It consists of:
- new fields in struct net_device:
+ hw_features - features that hw/driver supports toggling
+ wanted_features - features that user wants enabled, when possible
- new netdev_ops:
+ feat = ndo_fix_features(dev, feat) - API checking constraints for
enabling features or their combinations
+ ndo_set_features(dev) - API updating hardware state to match
changed dev->features
- new ethtool commands:
+ ETHTOOL_GFEATURES/ETHTOOL_SFEATURES: get/set dev->wanted_features
and trigger device reconfiguration if resulting dev->features
changed
+ ETHTOOL_GSTRINGS(ETH_SS_FEATURES): get feature bits names (meaning)
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/dev.c | 46 | ||||
-rw-r--r-- | net/core/ethtool.c | 125 |
2 files changed, 163 insertions, 8 deletions
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: |