summaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorMichał Mirosław <mirq-linux@rere.qmqm.pl>2011-02-22 16:52:29 +0000
committerDavid S. Miller <davem@davemloft.net>2011-02-23 14:23:32 -0800
commit39fc0ce5710c53bad14aaba1a789eec810c556f9 (patch)
treedc5ee565d2fe030dab7cdc1757c822ecc793cc4f /net/core
parent4e4db200541d49404ff39ac482efee072dd72144 (diff)
net: Implement SFEATURES compatibility for not updated drivers
Use discrete setting ops for not updated drivers. This will not make them conform to full G/SFEATURES semantics, though. 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/ethtool.c61
1 files changed, 61 insertions, 0 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 69a3edc182f..c1a71bb738d 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,
if (dev->ethtool_ops->get_rx_csum)
if (dev->ethtool_ops->get_rx_csum(dev))
features[0].active |= NETIF_F_RXCSUM;
+
+ /* mark legacy-changeable features */
+ if (dev->ethtool_ops->set_sg)
+ features[0].available |= NETIF_F_SG;
+ if (dev->ethtool_ops->set_tx_csum)
+ features[0].available |= NETIF_F_ALL_CSUM;
+ if (dev->ethtool_ops->set_tso)
+ features[0].available |= NETIF_F_ALL_TSO;
+ if (dev->ethtool_ops->set_rx_csum)
+ features[0].available |= NETIF_F_RXCSUM;
+ if (dev->ethtool_ops->set_flags)
+ features[0].available |= flags_dup_features;
+}
+
+static int ethtool_set_feature_compat(struct net_device *dev,
+ int (*legacy_set)(struct net_device *, u32),
+ struct ethtool_set_features_block *features, u32 mask)
+{
+ u32 do_set;
+
+ if (!legacy_set)
+ return 0;
+
+ if (!(features[0].valid & mask))
+ return 0;
+
+ features[0].valid &= ~mask;
+
+ do_set = !!(features[0].requested & mask);
+
+ if (legacy_set(dev, do_set) < 0)
+ netdev_info(dev,
+ "Legacy feature change (%s) failed for 0x%08x\n",
+ do_set ? "set" : "clear", mask);
+
+ return 1;
+}
+
+static int ethtool_set_features_compat(struct net_device *dev,
+ struct ethtool_set_features_block *features)
+{
+ int compat;
+
+ if (!dev->ethtool_ops)
+ return 0;
+
+ compat = ethtool_set_feature_compat(dev, dev->ethtool_ops->set_sg,
+ features, NETIF_F_SG);
+ compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tx_csum,
+ features, NETIF_F_ALL_CSUM);
+ compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tso,
+ features, NETIF_F_ALL_TSO);
+ compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_rx_csum,
+ features, NETIF_F_RXCSUM);
+ compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_flags,
+ features, flags_dup_features);
+
+ return compat;
}
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)
if (features[0].valid & ~NETIF_F_ETHTOOL_BITS)
return -EINVAL;
+ if (ethtool_set_features_compat(dev, features))
+ ret |= ETHTOOL_F_COMPAT;
+
if (features[0].valid & ~dev->hw_features) {
features[0].valid &= dev->hw_features;
ret |= ETHTOOL_F_UNSUPPORTED;