* @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,
        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
 #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.
        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)
 {
 
 }
 EXPORT_SYMBOL(netdev_rss_key_fill);
 
+static int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max)
+{
+       u32 dev_size, current_max = 0;
+       u32 *indir;
+       int ret;
+
+       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;
+
+       while (dev_size--)
+               current_max = max(current_max, indir[dev_size]);
+
+       *max = current_max;
+
+out:
+       kfree(indir);
+       return ret;
+}
+
 static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
                                                     void __user *useraddr)
 {
        }
 
        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);
        }
 
        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);
                                                   void __user *useraddr)
 {
        struct ethtool_channels channels;
+       u32 max_rx_in_use = 0;
 
        if (!dev->ethtool_ops->set_channels)
                return -EOPNOTSUPP;
        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_in_use) &&
+           (channels.combined_count + channels.rx_count) <= max_rx_in_use)
+           return -EINVAL;
+
        return dev->ethtool_ops->set_channels(dev, &channels);
 }