diff mbox

[1/4] ethtool: correctly ensure {GS}CHANNELS doesn't conflict with GS{RXFH}

Message ID 1454961965-15116-2-git-send-email-jacob.e.keller@intel.com
State Superseded, archived
Delegated to: David Miller
Headers show

Commit Message

Jacob Keller Feb. 8, 2016, 8:06 p.m. UTC
Ethernet drivers implementing both {GS}RXFH and {GS}CHANNELS ethtool ops
incorrectly allow SCHANNELS when it would conflict with the settings
from SRXFH. This occurs because it is not possible for drivers to
understand whether their Rx flow indirection table has been configured
or is in the default state. In addition, drivers currently behave in
various ways when increasing the number of Rx channels.

Some drivers will always destroy the Rx flow indirection table when this
occurs, whether it has been set by the user or not. Other drivers will
attempt to preserve the table even if the user has never modified it
from the default driver settings. Neither of these situation is
desirable because it leads to unexpected behavior or loss of user
configuration.

The correct behavior is to simply return -EINVAL when SCHANNELS would
conflict with the current Rx flow table settings. However, it should
only do so if the current settings were modified by the user. If we
required that the new settings never conflict with the current (default)
Rx flow settings, we would force users to first reduce their Rx flow
settings and then reduce the number of Rx channels.

This patch proposes a solution implemented in net/core/ethtool.c which
ensures that all drivers behave correctly. It checks whether the RXFH
table has been configured to non-default settings, and stores this
information in a private netdev flag. When the number of channels is
requested to change, it first ensures that the current Rx flow table is
not going to assign flows to now disabled channels.

Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
 include/linux/netdevice.h |  8 +++++++
 net/core/ethtool.c        | 59 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)

Comments

kernel test robot Feb. 8, 2016, 8:24 p.m. UTC | #1
Hi Jacob,

[auto build test ERROR on net/master]
[also build test ERROR on v4.5-rc3 next-20160208]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Jacob-Keller/ethtool-correctly-ensure-GS-CHANNELS-doesn-t-conflict-with-GS-RXFH/20160209-041131
config: xtensa-allyesconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=xtensa 

All errors (new ones prefixed by >>):

   net/core/ethtool.c: In function 'ethtool_set_channels':
>> net/core/ethtool.c:1292:6: error: implicit declaration of function 'ethtool_get_max_rxfh_channel' [-Werror=implicit-function-declaration]
         ethtool_get_max_rxfh_channel(dev, &max_rx) &&
         ^
>> net/core/ethtool.c:1295:6: error: expected ')' before 'return'
         return -EINVAL;
         ^
>> net/core/ethtool.c:1298:1: error: expected expression before '}' token
    }
    ^
   net/core/ethtool.c:1281:6: warning: unused variable 'ret' [-Wunused-variable]
     int ret;
         ^
   net/core/ethtool.c:1298:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors

vim +/ethtool_get_max_rxfh_channel +1292 net/core/ethtool.c

  1286		if (copy_from_user(&channels, useraddr, sizeof(channels)))
  1287			return -EFAULT;
  1288	
  1289		/* ensure the new Rx count fits within the configured Rx flow
  1290		 * indirection table settings */
  1291		if (netif_is_rxfh_configured(dev) &&
> 1292		    ethtool_get_max_rxfh_channel(dev, &max_rx) &&
  1293		    ((channels.rx_count > max_rx) ||
  1294		     (channels.combined_count > max_rx))
> 1295		    return -EINVAL;
  1296	
  1297		return dev->ethtool_ops->set_channels(dev, &channels);
> 1298	}
  1299	
  1300	static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
  1301	{

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Feb. 8, 2016, 8:31 p.m. UTC | #2
Hi Jacob,

[auto build test ERROR on net/master]
[also build test ERROR on v4.5-rc3 next-20160208]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Jacob-Keller/ethtool-correctly-ensure-GS-CHANNELS-doesn-t-conflict-with-GS-RXFH/20160209-041131
config: x86_64-randconfig-x013-201606 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   net/core/ethtool.c: In function 'ethtool_set_channels':
>> net/core/ethtool.c:2143:0: error: unterminated argument list invoking macro "if"
    }
    ^
>> net/core/ethtool.c:1291:2: error: expected '(' at end of input
     if (netif_is_rxfh_configured(dev) &&
     ^
   net/core/ethtool.c:1291:2: error: expected declaration or statement at end of input
   net/core/ethtool.c:1281:6: warning: unused variable 'ret' [-Wunused-variable]
     int ret;
         ^
   net/core/ethtool.c:1280:6: warning: unused variable 'max_rx' [-Wunused-variable]
     u32 max_rx = 0;
         ^
   net/core/ethtool.c: At top level:
   net/core/ethtool.c:116:12: warning: 'ethtool_get_features' defined but not used [-Wunused-function]
    static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
               ^
   net/core/ethtool.c:154:12: warning: 'ethtool_set_features' defined but not used [-Wunused-function]
    static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
               ^
   net/core/ethtool.c:237:13: warning: '__ethtool_get_strings' defined but not used [-Wunused-function]
    static void __ethtool_get_strings(struct net_device *dev,
                ^
   net/core/ethtool.c:296:12: warning: 'ethtool_get_one_feature' defined but not used [-Wunused-function]
    static int ethtool_get_one_feature(struct net_device *dev,
               ^
   net/core/ethtool.c:310:12: warning: 'ethtool_set_one_feature' defined but not used [-Wunused-function]
    static int ethtool_set_one_feature(struct net_device *dev,
               ^
   net/core/ethtool.c:340:12: warning: '__ethtool_get_flags' defined but not used [-Wunused-function]
    static u32 __ethtool_get_flags(struct net_device *dev)
               ^
   net/core/ethtool.c:358:12: warning: '__ethtool_set_flags' defined but not used [-Wunused-function]
    static int __ethtool_set_flags(struct net_device *dev, u32 data)
               ^
   net/core/ethtool.c:402:12: warning: 'ethtool_get_settings' defined but not used [-Wunused-function]
    static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
               ^
   net/core/ethtool.c:416:12: warning: 'ethtool_set_settings' defined but not used [-Wunused-function]
    static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
               ^
   net/core/ethtool.c:429:31: warning: 'ethtool_get_drvinfo' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
                                  ^
   net/core/ethtool.c:475:31: warning: 'ethtool_get_sset_info' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev,
                                  ^
   net/core/ethtool.c:531:31: warning: 'ethtool_set_rxnfc' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
                                  ^
   net/core/ethtool.c:563:31: warning: 'ethtool_get_rxnfc' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
                                  ^
   net/core/ethtool.c:678:31: warning: 'ethtool_get_rxfh_indir' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
                                  ^
   net/core/ethtool.c:726:31: warning: 'ethtool_set_rxfh_indir' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
                                  ^
   net/core/ethtool.c:788:31: warning: 'ethtool_get_rxfh' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
                                  ^
   net/core/ethtool.c:860:31: warning: 'ethtool_set_rxfh' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
                                  ^
   net/core/ethtool.c:955:12: warning: 'ethtool_get_regs' defined but not used [-Wunused-function]
    static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
               ^
   net/core/ethtool.c:991:12: warning: 'ethtool_reset' defined but not used [-Wunused-function]
    static int ethtool_reset(struct net_device *dev, char __user *useraddr)
               ^
   net/core/ethtool.c:1011:12: warning: 'ethtool_get_wol' defined but not used [-Wunused-function]
    static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
               ^
   net/core/ethtool.c:1025:12: warning: 'ethtool_set_wol' defined but not used [-Wunused-function]
    static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
               ^
   net/core/ethtool.c:1038:12: warning: 'ethtool_get_eee' defined but not used [-Wunused-function]
    static int ethtool_get_eee(struct net_device *dev, char __user *useraddr)
               ^
   net/core/ethtool.c:1059:12: warning: 'ethtool_set_eee' defined but not used [-Wunused-function]
    static int ethtool_set_eee(struct net_device *dev, char __user *useraddr)
               ^
   net/core/ethtool.c:1072:12: warning: 'ethtool_nway_reset' defined but not used [-Wunused-function]
    static int ethtool_nway_reset(struct net_device *dev)
               ^
   net/core/ethtool.c:1080:12: warning: 'ethtool_get_link' defined but not used [-Wunused-function]
    static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
               ^
   net/core/ethtool.c:1145:12: warning: 'ethtool_get_eeprom' defined but not used [-Wunused-function]
    static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
               ^
   net/core/ethtool.c:1157:12: warning: 'ethtool_set_eeprom' defined but not used [-Wunused-function]
    static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
               ^
   net/core/ethtool.c:1205:31: warning: 'ethtool_get_coalesce' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev,
                                  ^
   net/core/ethtool.c:1220:31: warning: 'ethtool_set_coalesce' defined but not used [-Wunused-function]
    static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
                                  ^
   net/core/ethtool.c:1234:12: warning: 'ethtool_get_ringparam' defined but not used [-Wunused-function]
    static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
               ^

vim +/if +2143 net/core/ethtool.c

^1da177e Linus Torvalds    2005-04-16  2137  		dev->ethtool_ops->complete(dev);
d8a33ac4 Stephen Hemminger 2005-05-29  2138  
d8a33ac4 Stephen Hemminger 2005-05-29  2139  	if (old_features != dev->features)
d8a33ac4 Stephen Hemminger 2005-05-29  2140  		netdev_features_change(dev);
d8a33ac4 Stephen Hemminger 2005-05-29  2141  
^1da177e Linus Torvalds    2005-04-16  2142  	return rc;
^1da177e Linus Torvalds    2005-04-16 @2143  }

:::::: The code at line 2143 was first introduced by commit
:::::: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Linux-2.6.12-rc2

:::::: TO: Linus Torvalds <torvalds@ppc970.osdl.org>
:::::: CC: Linus Torvalds <torvalds@ppc970.osdl.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Jakub KiciƄski Feb. 8, 2016, 8:47 p.m. UTC | #3
Build bot seems upset so let me throw few stones as well.

On Mon,  8 Feb 2016 12:06:02 -0800, Jacob Keller wrote:
>
> +static inline int ethool_get_max_rxfh_channel(struct net_device *dev, u32 *max)

static inline in C sources is frowned upon.

> +	u32 dev_size, current_max = 0;
> +	u32 *indir;
> +	int ret, i;
> +
> +	if (!dev->ethtool_ops->get_rxfh_indir_size ||
> +	    !dev->ethtool_ops->get_rxfh)
> +		return -EOPNOTSUPP;
> +	dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
> +	if (dev_size == 0)
> +		return -EOPNOTSUPP;
> +
> +	indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
> +	if (!indir)
> +		return -ENOMEM;
> +
> +	ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
> +	if (ret)
> +		goto out;
> +
> +	for (i = dev_size; i--;) {
> +		if (indir[i] > current_max)
> +			current_max = indir[i];
> +	}

Could be

while (dev_size--)
	current_max = max(current_max, indir[dev_size]);

> +	/* ensure the new Rx count fits within the configured Rx flow
> +	 * indirection table settings */
> +	if (netif_is_rxfh_configured(dev) &&
> +	    ethtool_get_max_rxfh_channel(dev, &max_rx) &&
> +	    ((channels.rx_count > max_rx) ||
> +	     (channels.combined_count > max_rx))
> +	    return -EINVAL;
> +
>  	return dev->ethtool_ops->set_channels(dev, &channels);
>  }

The situation with rx_count vs combined count is unclear.  My current
understanding is that

channels.combined_count + channels.rx_count > max_rx

would be the safest way to go.
diff mbox

Patch

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 289c2314d766..044fa2d88c7f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1291,6 +1291,7 @@  struct net_device_ops {
  * @IFF_OPENVSWITCH: device is a Open vSwitch master
  * @IFF_L3MDEV_SLAVE: device is enslaved to an L3 master device
  * @IFF_TEAM: device is a team device
+ * @IFF_RXFH_CONFIGURED: device has had Rx Flow indirection table configured
  */
 enum netdev_priv_flags {
 	IFF_802_1Q_VLAN			= 1<<0,
@@ -1318,6 +1319,7 @@  enum netdev_priv_flags {
 	IFF_OPENVSWITCH			= 1<<22,
 	IFF_L3MDEV_SLAVE		= 1<<23,
 	IFF_TEAM			= 1<<24,
+	IFF_RXFH_CONFIGURED		= 1<<25,
 };
 
 #define IFF_802_1Q_VLAN			IFF_802_1Q_VLAN
@@ -1345,6 +1347,7 @@  enum netdev_priv_flags {
 #define IFF_OPENVSWITCH			IFF_OPENVSWITCH
 #define IFF_L3MDEV_SLAVE		IFF_L3MDEV_SLAVE
 #define IFF_TEAM			IFF_TEAM
+#define IFF_RXFH_CONFIGURED		IFF_RXFH_CONFIGURED
 
 /**
  *	struct net_device - The DEVICE structure.
@@ -4045,6 +4048,11 @@  static inline bool netif_is_lag_port(const struct net_device *dev)
 	return netif_is_bond_slave(dev) || netif_is_team_port(dev);
 }
 
+static inline bool netif_is_rxfh_configured(const struct net_device *dev)
+{
+	return dev->priv_flags & IFF_RXFH_CONFIGURED;
+}
+
 /* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */
 static inline void netif_keep_dst(struct net_device *dev)
 {
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index daf04709dd3c..8a40948c1fc6 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -642,6 +642,39 @@  void netdev_rss_key_fill(void *buffer, size_t len)
 }
 EXPORT_SYMBOL(netdev_rss_key_fill);
 
+static inline int ethool_get_max_rxfh_channel(struct net_device *dev, u32 *max)
+{
+	u32 dev_size, current_max = 0;
+	u32 *indir;
+	int ret, i;
+
+	if (!dev->ethtool_ops->get_rxfh_indir_size ||
+	    !dev->ethtool_ops->get_rxfh)
+		return -EOPNOTSUPP;
+	dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
+	if (dev_size == 0)
+		return -EOPNOTSUPP;
+
+	indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
+	if (!indir)
+		return -ENOMEM;
+
+	ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
+	if (ret)
+		goto out;
+
+	for (i = dev_size; i--;) {
+		if (indir[i] > current_max)
+			current_max = indir[i];
+	}
+
+	*max = current_max;
+
+out:
+	kfree(indir);
+	return ret;
+}
+
 static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
 						     void __user *useraddr)
 {
@@ -738,6 +771,14 @@  static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
 	}
 
 	ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
+	if (ret)
+		goto out;
+
+	/* indicate whether rxfh was set to default */
+	if (user_size == 0)
+		dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
+	else
+		dev->priv_flags |= IFF_RXFH_CONFIGURED;
 
 out:
 	kfree(indir);
@@ -897,6 +938,14 @@  static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
 	}
 
 	ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
+	if (ret)
+		goto out;
+
+	/* indicate whether rxfh was set to default */
+	if (rxfh.indir_size == 0)
+		dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
+	else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
+		dev->priv_flags |= IFF_RXFH_CONFIGURED;
 
 out:
 	kfree(rss_config);
@@ -1228,6 +1277,8 @@  static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
 						   void __user *useraddr)
 {
 	struct ethtool_channels channels;
+	u32 max_rx = 0;
+	int ret;
 
 	if (!dev->ethtool_ops->set_channels)
 		return -EOPNOTSUPP;
@@ -1235,6 +1286,14 @@  static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
 	if (copy_from_user(&channels, useraddr, sizeof(channels)))
 		return -EFAULT;
 
+	/* ensure the new Rx count fits within the configured Rx flow
+	 * indirection table settings */
+	if (netif_is_rxfh_configured(dev) &&
+	    ethtool_get_max_rxfh_channel(dev, &max_rx) &&
+	    ((channels.rx_count > max_rx) ||
+	     (channels.combined_count > max_rx))
+	    return -EINVAL;
+
 	return dev->ethtool_ops->set_channels(dev, &channels);
 }