aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorMichał Mirosław <mirq-linux@rere.qmqm.pl>2011-02-15 11:59:17 -0500
committerDavid S. Miller <davem@davemloft.net>2011-02-17 17:16:33 -0500
commit5455c6998d34dc983a8693500e4dffefc3682dc5 (patch)
treeb765aecf6d33d8c550cde78368ccc8654951ec07 /net/core
parent0a417704777ed29d0e8c72b7274a328e61248e75 (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.c46
-rw-r--r--net/core/ethtool.c125
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}
5303EXPORT_SYMBOL(netdev_fix_features); 5303EXPORT_SYMBOL(netdev_fix_features);
5304 5304
5305void 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}
5334EXPORT_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
177static 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
210static 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
244static 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
175static int __ethtool_get_sset_count(struct net_device *dev, int sset) 282static 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
194static u32 ethtool_get_feature_mask(u32 eth_cmd) 308static 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: