net: Implement SFEATURES compatibility for not updated drivers
authorMichał Mirosław <mirq-linux@rere.qmqm.pl>
Tue, 22 Feb 2011 16:52:29 +0000 (16:52 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 23 Feb 2011 22:23:32 +0000 (14:23 -0800)
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>
include/linux/ethtool.h
net/core/ethtool.c

index 54d776c2c1b5d7a84e592a34c368c22414c67f22..aac3e2eeb4fd626565462379c0acd7fb4d1e1c66 100644 (file)
@@ -591,6 +591,9 @@ struct ethtool_sfeatures {
  *      Probably there are other device-specific constraints on some features
  *      in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered
  *      here as though ignored bits were cleared.
+ *   %ETHTOOL_F_COMPAT - some or all changes requested were made by calling
+ *      compatibility functions. Requested offload state cannot be properly
+ *      managed by kernel.
  *
  * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of
  * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands
@@ -600,10 +603,12 @@ struct ethtool_sfeatures {
 enum ethtool_sfeatures_retval_bits {
        ETHTOOL_F_UNSUPPORTED__BIT,
        ETHTOOL_F_WISH__BIT,
+       ETHTOOL_F_COMPAT__BIT,
 };
 
 #define ETHTOOL_F_UNSUPPORTED   (1 << ETHTOOL_F_UNSUPPORTED__BIT)
 #define ETHTOOL_F_WISH          (1 << ETHTOOL_F_WISH__BIT)
+#define ETHTOOL_F_COMPAT        (1 << ETHTOOL_F_COMPAT__BIT)
 
 #ifdef __KERNEL__
 
index 69a3edc182f9952b043cff344f0d50382038cf02..c1a71bb738da4db6c8cd62b75d36b5d091316fd8 100644 (file)
@@ -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;