aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEyal Perry <eyalpe@mellanox.com>2014-12-02 11:12:10 -0500
committerDavid S. Miller <davem@davemloft.net>2014-12-08 21:07:10 -0500
commit892311f66f2411b813ca631009356891a0c2b0a1 (patch)
tree42267098964290cf349abd6733267c24867f2318
parent18b5427ae1654803930b97590ac5a85245861646 (diff)
ethtool: Support for configurable RSS hash function
This patch extends the set/get_rxfh ethtool-options for getting or setting the RSS hash function. It modifies drivers implementation of set/get_rxfh accordingly. This change also delegates the responsibility of checking whether a modification to a certain RX flow hash parameter is supported to the driver implementation of set_rxfh. User-kernel API is done through the new hfunc bitmask field in the ethtool_rxfh struct. A bit set in the hfunc field is corresponding to an index in the new string-set ETH_SS_RSS_HASH_FUNCS. Got approval from most of the relevant driver maintainers that their driver is using Toeplitz, and for the few that didn't answered, also assumed it is Toeplitz. Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Ariel Elior <ariel.elior@qlogic.com> Cc: Prashant Sreedharan <prashant@broadcom.com> Cc: Michael Chan <mchan@broadcom.com> Cc: Hariprasad S <hariprasad@chelsio.com> Cc: Sathya Perla <sathya.perla@emulex.com> Cc: Subbu Seetharaman <subbu.seetharaman@emulex.com> Cc: Ajit Khaparde <ajit.khaparde@emulex.com> Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com> Cc: Jesse Brandeburg <jesse.brandeburg@intel.com> Cc: Bruce Allan <bruce.w.allan@intel.com> Cc: Carolyn Wyborny <carolyn.wyborny@intel.com> Cc: Don Skidmore <donald.c.skidmore@intel.com> Cc: Greg Rose <gregory.v.rose@intel.com> Cc: Matthew Vick <matthew.vick@intel.com> Cc: John Ronciak <john.ronciak@intel.com> Cc: Mitch Williams <mitch.a.williams@intel.com> Cc: Amir Vadai <amirv@mellanox.com> Cc: Solarflare linux maintainers <linux-net-drivers@solarflare.com> Cc: Shradha Shah <sshah@solarflare.com> Cc: Shreyas Bhatewara <sbhatewara@vmware.com> Cc: "VMware, Inc." <pv-drivers@vmware.com> Cc: Ben Hutchings <ben@decadent.org.uk> Signed-off-by: Eyal Perry <eyalpe@mellanox.com> Signed-off-by: Amir Vadai <amirv@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c11
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c20
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c20
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c18
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c12
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c12
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c17
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c16
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c13
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c18
-rw-r--r--drivers/net/vmxnet3/vmxnet3_ethtool.c15
-rw-r--r--include/linux/ethtool.h42
-rw-r--r--include/uapi/linux/ethtool.h10
-rw-r--r--net/core/ethtool.c69
14 files changed, 223 insertions, 70 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
index 95d44538357f..ebf489351555 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
@@ -511,7 +511,8 @@ static u32 xgbe_get_rxfh_indir_size(struct net_device *netdev)
511 return ARRAY_SIZE(pdata->rss_table); 511 return ARRAY_SIZE(pdata->rss_table);
512} 512}
513 513
514static int xgbe_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) 514static int xgbe_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
515 u8 *hfunc)
515{ 516{
516 struct xgbe_prv_data *pdata = netdev_priv(netdev); 517 struct xgbe_prv_data *pdata = netdev_priv(netdev);
517 unsigned int i; 518 unsigned int i;
@@ -525,16 +526,22 @@ static int xgbe_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key)
525 if (key) 526 if (key)
526 memcpy(key, pdata->rss_key, sizeof(pdata->rss_key)); 527 memcpy(key, pdata->rss_key, sizeof(pdata->rss_key));
527 528
529 if (hfunc)
530 *hfunc = ETH_RSS_HASH_TOP;
531
528 return 0; 532 return 0;
529} 533}
530 534
531static int xgbe_set_rxfh(struct net_device *netdev, const u32 *indir, 535static int xgbe_set_rxfh(struct net_device *netdev, const u32 *indir,
532 const u8 *key) 536 const u8 *key, const u8 hfunc)
533{ 537{
534 struct xgbe_prv_data *pdata = netdev_priv(netdev); 538 struct xgbe_prv_data *pdata = netdev_priv(netdev);
535 struct xgbe_hw_if *hw_if = &pdata->hw_if; 539 struct xgbe_hw_if *hw_if = &pdata->hw_if;
536 unsigned int ret; 540 unsigned int ret;
537 541
542 if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
543 return -EOPNOTSUPP;
544
538 if (indir) { 545 if (indir) {
539 ret = hw_if->set_rss_lookup_table(pdata, indir); 546 ret = hw_if->set_rss_lookup_table(pdata, indir);
540 if (ret) 547 if (ret)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index 1edc931b1458..ffe4e003e636 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -3358,12 +3358,18 @@ static u32 bnx2x_get_rxfh_indir_size(struct net_device *dev)
3358 return T_ETH_INDIRECTION_TABLE_SIZE; 3358 return T_ETH_INDIRECTION_TABLE_SIZE;
3359} 3359}
3360 3360
3361static int bnx2x_get_rxfh(struct net_device *dev, u32 *indir, u8 *key) 3361static int bnx2x_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
3362 u8 *hfunc)
3362{ 3363{
3363 struct bnx2x *bp = netdev_priv(dev); 3364 struct bnx2x *bp = netdev_priv(dev);
3364 u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0}; 3365 u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
3365 size_t i; 3366 size_t i;
3366 3367
3368 if (hfunc)
3369 *hfunc = ETH_RSS_HASH_TOP;
3370 if (!indir)
3371 return 0;
3372
3367 /* Get the current configuration of the RSS indirection table */ 3373 /* Get the current configuration of the RSS indirection table */
3368 bnx2x_get_rss_ind_table(&bp->rss_conf_obj, ind_table); 3374 bnx2x_get_rss_ind_table(&bp->rss_conf_obj, ind_table);
3369 3375
@@ -3383,11 +3389,21 @@ static int bnx2x_get_rxfh(struct net_device *dev, u32 *indir, u8 *key)
3383} 3389}
3384 3390
3385static int bnx2x_set_rxfh(struct net_device *dev, const u32 *indir, 3391static int bnx2x_set_rxfh(struct net_device *dev, const u32 *indir,
3386 const u8 *key) 3392 const u8 *key, const u8 hfunc)
3387{ 3393{
3388 struct bnx2x *bp = netdev_priv(dev); 3394 struct bnx2x *bp = netdev_priv(dev);
3389 size_t i; 3395 size_t i;
3390 3396
3397 /* We require at least one supported parameter to be changed and no
3398 * change in any of the unsupported parameters
3399 */
3400 if (key ||
3401 (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
3402 return -EOPNOTSUPP;
3403
3404 if (!indir)
3405 return 0;
3406
3391 for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) { 3407 for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) {
3392 /* 3408 /*
3393 * The same as in bnx2x_get_rxfh: we can't use a memcpy() 3409 * The same as in bnx2x_get_rxfh: we can't use a memcpy()
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 43fd1b72c1ea..bb48a610b72a 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -12561,22 +12561,38 @@ static u32 tg3_get_rxfh_indir_size(struct net_device *dev)
12561 return size; 12561 return size;
12562} 12562}
12563 12563
12564static int tg3_get_rxfh(struct net_device *dev, u32 *indir, u8 *key) 12564static int tg3_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, u8 *hfunc)
12565{ 12565{
12566 struct tg3 *tp = netdev_priv(dev); 12566 struct tg3 *tp = netdev_priv(dev);
12567 int i; 12567 int i;
12568 12568
12569 if (hfunc)
12570 *hfunc = ETH_RSS_HASH_TOP;
12571 if (!indir)
12572 return 0;
12573
12569 for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++) 12574 for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++)
12570 indir[i] = tp->rss_ind_tbl[i]; 12575 indir[i] = tp->rss_ind_tbl[i];
12571 12576
12572 return 0; 12577 return 0;
12573} 12578}
12574 12579
12575static int tg3_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key) 12580static int tg3_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key,
12581 const u8 hfunc)
12576{ 12582{
12577 struct tg3 *tp = netdev_priv(dev); 12583 struct tg3 *tp = netdev_priv(dev);
12578 size_t i; 12584 size_t i;
12579 12585
12586 /* We require at least one supported parameter to be changed and no
12587 * change in any of the unsupported parameters
12588 */
12589 if (key ||
12590 (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
12591 return -EOPNOTSUPP;
12592
12593 if (!indir)
12594 return 0;
12595
12580 for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++) 12596 for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++)
12581 tp->rss_ind_tbl[i] = indir[i]; 12597 tp->rss_ind_tbl[i] = indir[i];
12582 12598
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 3aea82bb9039..e7342bc85026 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -2923,21 +2923,35 @@ static u32 get_rss_table_size(struct net_device *dev)
2923 return pi->rss_size; 2923 return pi->rss_size;
2924} 2924}
2925 2925
2926static int get_rss_table(struct net_device *dev, u32 *p, u8 *key) 2926static int get_rss_table(struct net_device *dev, u32 *p, u8 *key, u8 *hfunc)
2927{ 2927{
2928 const struct port_info *pi = netdev_priv(dev); 2928 const struct port_info *pi = netdev_priv(dev);
2929 unsigned int n = pi->rss_size; 2929 unsigned int n = pi->rss_size;
2930 2930
2931 if (hfunc)
2932 *hfunc = ETH_RSS_HASH_TOP;
2933 if (!p)
2934 return 0;
2931 while (n--) 2935 while (n--)
2932 p[n] = pi->rss[n]; 2936 p[n] = pi->rss[n];
2933 return 0; 2937 return 0;
2934} 2938}
2935 2939
2936static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key) 2940static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key,
2941 const u8 hfunc)
2937{ 2942{
2938 unsigned int i; 2943 unsigned int i;
2939 struct port_info *pi = netdev_priv(dev); 2944 struct port_info *pi = netdev_priv(dev);
2940 2945
2946 /* We require at least one supported parameter to be changed and no
2947 * change in any of the unsupported parameters
2948 */
2949 if (key ||
2950 (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
2951 return -EOPNOTSUPP;
2952 if (!p)
2953 return 0;
2954
2941 for (i = 0; i < pi->rss_size; i++) 2955 for (i = 0; i < pi->rss_size; i++)
2942 pi->rss[i] = p[i]; 2956 pi->rss[i] = p[i];
2943 if (pi->adapter->flags & FULL_INIT_DONE) 2957 if (pi->adapter->flags & FULL_INIT_DONE)
diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c
index e42a791c1835..73a500ccbf69 100644
--- a/drivers/net/ethernet/emulex/benet/be_ethtool.c
+++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c
@@ -1171,7 +1171,8 @@ static u32 be_get_rxfh_key_size(struct net_device *netdev)
1171 return RSS_HASH_KEY_LEN; 1171 return RSS_HASH_KEY_LEN;
1172} 1172}
1173 1173
1174static int be_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey) 1174static int be_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey,
1175 u8 *hfunc)
1175{ 1176{
1176 struct be_adapter *adapter = netdev_priv(netdev); 1177 struct be_adapter *adapter = netdev_priv(netdev);
1177 int i; 1178 int i;
@@ -1185,16 +1186,23 @@ static int be_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey)
1185 if (hkey) 1186 if (hkey)
1186 memcpy(hkey, rss->rss_hkey, RSS_HASH_KEY_LEN); 1187 memcpy(hkey, rss->rss_hkey, RSS_HASH_KEY_LEN);
1187 1188
1189 if (hfunc)
1190 *hfunc = ETH_RSS_HASH_TOP;
1191
1188 return 0; 1192 return 0;
1189} 1193}
1190 1194
1191static int be_set_rxfh(struct net_device *netdev, const u32 *indir, 1195static int be_set_rxfh(struct net_device *netdev, const u32 *indir,
1192 const u8 *hkey) 1196 const u8 *hkey, const u8 hfunc)
1193{ 1197{
1194 int rc = 0, i, j; 1198 int rc = 0, i, j;
1195 struct be_adapter *adapter = netdev_priv(netdev); 1199 struct be_adapter *adapter = netdev_priv(netdev);
1196 u8 rsstable[RSS_INDIR_TABLE_LEN]; 1200 u8 rsstable[RSS_INDIR_TABLE_LEN];
1197 1201
1202 /* We do not allow change in unsupported parameters */
1203 if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
1204 return -EOPNOTSUPP;
1205
1198 if (indir) { 1206 if (indir) {
1199 struct be_rx_obj *rxo; 1207 struct be_rx_obj *rxo;
1200 1208
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
index 2d04464e6aa3..651f53bc7376 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
@@ -916,11 +916,15 @@ static u32 fm10k_get_rssrk_size(struct net_device *netdev)
916 return FM10K_RSSRK_SIZE * FM10K_RSSRK_ENTRIES_PER_REG; 916 return FM10K_RSSRK_SIZE * FM10K_RSSRK_ENTRIES_PER_REG;
917} 917}
918 918
919static int fm10k_get_rssh(struct net_device *netdev, u32 *indir, u8 *key) 919static int fm10k_get_rssh(struct net_device *netdev, u32 *indir, u8 *key,
920 u8 *hfunc)
920{ 921{
921 struct fm10k_intfc *interface = netdev_priv(netdev); 922 struct fm10k_intfc *interface = netdev_priv(netdev);
922 int i, err; 923 int i, err;
923 924
925 if (hfunc)
926 *hfunc = ETH_RSS_HASH_TOP;
927
924 err = fm10k_get_reta(netdev, indir); 928 err = fm10k_get_reta(netdev, indir);
925 if (err || !key) 929 if (err || !key)
926 return err; 930 return err;
@@ -932,12 +936,16 @@ static int fm10k_get_rssh(struct net_device *netdev, u32 *indir, u8 *key)
932} 936}
933 937
934static int fm10k_set_rssh(struct net_device *netdev, const u32 *indir, 938static int fm10k_set_rssh(struct net_device *netdev, const u32 *indir,
935 const u8 *key) 939 const u8 *key, const u8 hfunc)
936{ 940{
937 struct fm10k_intfc *interface = netdev_priv(netdev); 941 struct fm10k_intfc *interface = netdev_priv(netdev);
938 struct fm10k_hw *hw = &interface->hw; 942 struct fm10k_hw *hw = &interface->hw;
939 int i, err; 943 int i, err;
940 944
945 /* We do not allow change in unsupported parameters */
946 if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
947 return -EOPNOTSUPP;
948
941 err = fm10k_set_reta(netdev, indir); 949 err = fm10k_set_reta(netdev, indir);
942 if (err || !key) 950 if (err || !key)
943 return err; 951 return err;
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
index 69a269b23be6..69b97bac182c 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
@@ -627,13 +627,19 @@ static u32 i40evf_get_rxfh_indir_size(struct net_device *netdev)
627 * 627 *
628 * Reads the indirection table directly from the hardware. Always returns 0. 628 * Reads the indirection table directly from the hardware. Always returns 0.
629 **/ 629 **/
630static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) 630static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
631 u8 *hfunc)
631{ 632{
632 struct i40evf_adapter *adapter = netdev_priv(netdev); 633 struct i40evf_adapter *adapter = netdev_priv(netdev);
633 struct i40e_hw *hw = &adapter->hw; 634 struct i40e_hw *hw = &adapter->hw;
634 u32 hlut_val; 635 u32 hlut_val;
635 int i, j; 636 int i, j;
636 637
638 if (hfunc)
639 *hfunc = ETH_RSS_HASH_TOP;
640 if (!indir)
641 return 0;
642
637 for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) { 643 for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
638 hlut_val = rd32(hw, I40E_VFQF_HLUT(i)); 644 hlut_val = rd32(hw, I40E_VFQF_HLUT(i));
639 indir[j++] = hlut_val & 0xff; 645 indir[j++] = hlut_val & 0xff;
@@ -654,13 +660,20 @@ static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key)
654 * returns 0 after programming the table. 660 * returns 0 after programming the table.
655 **/ 661 **/
656static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, 662static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir,
657 const u8 *key) 663 const u8 *key, const u8 hfunc)
658{ 664{
659 struct i40evf_adapter *adapter = netdev_priv(netdev); 665 struct i40evf_adapter *adapter = netdev_priv(netdev);
660 struct i40e_hw *hw = &adapter->hw; 666 struct i40e_hw *hw = &adapter->hw;
661 u32 hlut_val; 667 u32 hlut_val;
662 int i, j; 668 int i, j;
663 669
670 /* We do not allow change in unsupported parameters */
671 if (key ||
672 (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
673 return -EOPNOTSUPP;
674 if (!indir)
675 return 0;
676
664 for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) { 677 for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
665 hlut_val = indir[j++]; 678 hlut_val = indir[j++];
666 hlut_val |= indir[j++] << 8; 679 hlut_val |= indir[j++] << 8;
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 02cfd3b14762..d5673eb90c54 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -2842,11 +2842,16 @@ static u32 igb_get_rxfh_indir_size(struct net_device *netdev)
2842 return IGB_RETA_SIZE; 2842 return IGB_RETA_SIZE;
2843} 2843}
2844 2844
2845static int igb_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) 2845static int igb_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
2846 u8 *hfunc)
2846{ 2847{
2847 struct igb_adapter *adapter = netdev_priv(netdev); 2848 struct igb_adapter *adapter = netdev_priv(netdev);
2848 int i; 2849 int i;
2849 2850
2851 if (hfunc)
2852 *hfunc = ETH_RSS_HASH_TOP;
2853 if (!indir)
2854 return 0;
2850 for (i = 0; i < IGB_RETA_SIZE; i++) 2855 for (i = 0; i < IGB_RETA_SIZE; i++)
2851 indir[i] = adapter->rss_indir_tbl[i]; 2856 indir[i] = adapter->rss_indir_tbl[i];
2852 2857
@@ -2889,13 +2894,20 @@ void igb_write_rss_indir_tbl(struct igb_adapter *adapter)
2889} 2894}
2890 2895
2891static int igb_set_rxfh(struct net_device *netdev, const u32 *indir, 2896static int igb_set_rxfh(struct net_device *netdev, const u32 *indir,
2892 const u8 *key) 2897 const u8 *key, const u8 hfunc)
2893{ 2898{
2894 struct igb_adapter *adapter = netdev_priv(netdev); 2899 struct igb_adapter *adapter = netdev_priv(netdev);
2895 struct e1000_hw *hw = &adapter->hw; 2900 struct e1000_hw *hw = &adapter->hw;
2896 int i; 2901 int i;
2897 u32 num_queues; 2902 u32 num_queues;
2898 2903
2904 /* We do not allow change in unsupported parameters */
2905 if (key ||
2906 (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
2907 return -EOPNOTSUPP;
2908 if (!indir)
2909 return 0;
2910
2899 num_queues = adapter->rss_queues; 2911 num_queues = adapter->rss_queues;
2900 2912
2901 switch (hw->mac.type) { 2913 switch (hw->mac.type) {
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index c45e06abc073..28c3fc5a0791 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -978,7 +978,8 @@ static u32 mlx4_en_get_rxfh_key_size(struct net_device *netdev)
978 return MLX4_EN_RSS_KEY_SIZE; 978 return MLX4_EN_RSS_KEY_SIZE;
979} 979}
980 980
981static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key) 981static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key,
982 u8 *hfunc)
982{ 983{
983 struct mlx4_en_priv *priv = netdev_priv(dev); 984 struct mlx4_en_priv *priv = netdev_priv(dev);
984 struct mlx4_en_rss_map *rss_map = &priv->rss_map; 985 struct mlx4_en_rss_map *rss_map = &priv->rss_map;
@@ -990,16 +991,20 @@ static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key)
990 rss_rings = 1 << ilog2(rss_rings); 991 rss_rings = 1 << ilog2(rss_rings);
991 992
992 while (n--) { 993 while (n--) {
994 if (!ring_index)
995 break;
993 ring_index[n] = rss_map->qps[n % rss_rings].qpn - 996 ring_index[n] = rss_map->qps[n % rss_rings].qpn -
994 rss_map->base_qpn; 997 rss_map->base_qpn;
995 } 998 }
996 if (key) 999 if (key)
997 memcpy(key, priv->rss_key, MLX4_EN_RSS_KEY_SIZE); 1000 memcpy(key, priv->rss_key, MLX4_EN_RSS_KEY_SIZE);
1001 if (hfunc)
1002 *hfunc = ETH_RSS_HASH_TOP;
998 return err; 1003 return err;
999} 1004}
1000 1005
1001static int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index, 1006static int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index,
1002 const u8 *key) 1007 const u8 *key, const u8 hfunc)
1003{ 1008{
1004 struct mlx4_en_priv *priv = netdev_priv(dev); 1009 struct mlx4_en_priv *priv = netdev_priv(dev);
1005 struct mlx4_en_dev *mdev = priv->mdev; 1010 struct mlx4_en_dev *mdev = priv->mdev;
@@ -1008,6 +1013,10 @@ static int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index,
1008 int i; 1013 int i;
1009 int rss_rings = 0; 1014 int rss_rings = 0;
1010 1015
1016 /* We do not allow change in unsupported parameters */
1017 if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
1018 return -EOPNOTSUPP;
1019
1011 /* Calculate RSS table size and make sure flows are spread evenly 1020 /* Calculate RSS table size and make sure flows are spread evenly
1012 * between rings 1021 * between rings
1013 */ 1022 */
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index cad258a78708..4835bc0d0de8 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -1086,19 +1086,29 @@ static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
1086 0 : ARRAY_SIZE(efx->rx_indir_table)); 1086 0 : ARRAY_SIZE(efx->rx_indir_table));
1087} 1087}
1088 1088
1089static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key) 1089static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key,
1090 u8 *hfunc)
1090{ 1091{
1091 struct efx_nic *efx = netdev_priv(net_dev); 1092 struct efx_nic *efx = netdev_priv(net_dev);
1092 1093
1093 memcpy(indir, efx->rx_indir_table, sizeof(efx->rx_indir_table)); 1094 if (hfunc)
1095 *hfunc = ETH_RSS_HASH_TOP;
1096 if (indir)
1097 memcpy(indir, efx->rx_indir_table, sizeof(efx->rx_indir_table));
1094 return 0; 1098 return 0;
1095} 1099}
1096 1100
1097static int efx_ethtool_set_rxfh(struct net_device *net_dev, 1101static int efx_ethtool_set_rxfh(struct net_device *net_dev, const u32 *indir,
1098 const u32 *indir, const u8 *key) 1102 const u8 *key, const u8 hfunc)
1099{ 1103{
1100 struct efx_nic *efx = netdev_priv(net_dev); 1104 struct efx_nic *efx = netdev_priv(net_dev);
1101 1105
1106 /* We do not allow change in unsupported parameters */
1107 if (key ||
1108 (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
1109 return -EOPNOTSUPP;
1110 if (!indir)
1111 return 0;
1102 memcpy(efx->rx_indir_table, indir, sizeof(efx->rx_indir_table)); 1112 memcpy(efx->rx_indir_table, indir, sizeof(efx->rx_indir_table));
1103 efx->type->rx_push_rss_config(efx); 1113 efx->type->rx_push_rss_config(efx);
1104 return 0; 1114 return 0;
diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c
index b725fd9e7803..b7b53329d575 100644
--- a/drivers/net/vmxnet3/vmxnet3_ethtool.c
+++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c
@@ -583,12 +583,16 @@ vmxnet3_get_rss_indir_size(struct net_device *netdev)
583} 583}
584 584
585static int 585static int
586vmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key) 586vmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key, u8 *hfunc)
587{ 587{
588 struct vmxnet3_adapter *adapter = netdev_priv(netdev); 588 struct vmxnet3_adapter *adapter = netdev_priv(netdev);
589 struct UPT1_RSSConf *rssConf = adapter->rss_conf; 589 struct UPT1_RSSConf *rssConf = adapter->rss_conf;
590 unsigned int n = rssConf->indTableSize; 590 unsigned int n = rssConf->indTableSize;
591 591
592 if (hfunc)
593 *hfunc = ETH_RSS_HASH_TOP;
594 if (!p)
595 return 0;
592 while (n--) 596 while (n--)
593 p[n] = rssConf->indTable[n]; 597 p[n] = rssConf->indTable[n];
594 return 0; 598 return 0;
@@ -596,13 +600,20 @@ vmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key)
596} 600}
597 601
598static int 602static int
599vmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key) 603vmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key,
604 const u8 hfunc)
600{ 605{
601 unsigned int i; 606 unsigned int i;
602 unsigned long flags; 607 unsigned long flags;
603 struct vmxnet3_adapter *adapter = netdev_priv(netdev); 608 struct vmxnet3_adapter *adapter = netdev_priv(netdev);
604 struct UPT1_RSSConf *rssConf = adapter->rss_conf; 609 struct UPT1_RSSConf *rssConf = adapter->rss_conf;
605 610
611 /* We do not allow change in unsupported parameters */
612 if (key ||
613 (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
614 return -EOPNOTSUPP;
615 if (!p)
616 return 0;
606 for (i = 0; i < rssConf->indTableSize; i++) 617 for (i = 0; i < rssConf->indTableSize; i++)
607 rssConf->indTable[i] = p[i]; 618 rssConf->indTable[i] = p[i];
608 619
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index c1a2d60dfb82..653dc9c4ebac 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -59,6 +59,26 @@ enum ethtool_phys_id_state {
59 ETHTOOL_ID_OFF 59 ETHTOOL_ID_OFF
60}; 60};
61 61
62enum {
63 ETH_RSS_HASH_TOP_BIT, /* Configurable RSS hash function - Toeplitz */
64 ETH_RSS_HASH_XOR_BIT, /* Configurable RSS hash function - Xor */
65
66 /*
67 * Add your fresh new hash function bits above and remember to update
68 * rss_hash_func_strings[] in ethtool.c
69 */
70 ETH_RSS_HASH_FUNCS_COUNT
71};
72
73#define __ETH_RSS_HASH_BIT(bit) ((u32)1 << (bit))
74#define __ETH_RSS_HASH(name) __ETH_RSS_HASH_BIT(ETH_RSS_HASH_##name##_BIT)
75
76#define ETH_RSS_HASH_TOP __ETH_RSS_HASH(TOP)
77#define ETH_RSS_HASH_XOR __ETH_RSS_HASH(XOR)
78
79#define ETH_RSS_HASH_UNKNOWN 0
80#define ETH_RSS_HASH_NO_CHANGE 0
81
62struct net_device; 82struct net_device;
63 83
64/* Some generic methods drivers may use in their ethtool_ops */ 84/* Some generic methods drivers may use in their ethtool_ops */
@@ -158,17 +178,14 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
158 * Returns zero if not supported for this specific device. 178 * Returns zero if not supported for this specific device.
159 * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table. 179 * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table.
160 * Returns zero if not supported for this specific device. 180 * Returns zero if not supported for this specific device.
161 * @get_rxfh: Get the contents of the RX flow hash indirection table and hash 181 * @get_rxfh: Get the contents of the RX flow hash indirection table, hash key
162 * key. 182 * and/or hash function.
163 * Will only be called if one or both of @get_rxfh_indir_size and
164 * @get_rxfh_key_size are implemented and return non-zero.
165 * Returns a negative error code or zero.
166 * @set_rxfh: Set the contents of the RX flow hash indirection table and/or
167 * hash key. In case only the indirection table or hash key is to be
168 * changed, the other argument will be %NULL.
169 * Will only be called if one or both of @get_rxfh_indir_size and
170 * @get_rxfh_key_size are implemented and return non-zero.
171 * Returns a negative error code or zero. 183 * Returns a negative error code or zero.
184 * @set_rxfh: Set the contents of the RX flow hash indirection table, hash
185 * key, and/or hash function. Arguments which are set to %NULL or zero
186 * will remain unchanged.
187 * Returns a negative error code or zero. An error code must be returned
188 * if at least one unsupported change was requested.
172 * @get_channels: Get number of channels. 189 * @get_channels: Get number of channels.
173 * @set_channels: Set number of channels. Returns a negative error code or 190 * @set_channels: Set number of channels. Returns a negative error code or
174 * zero. 191 * zero.
@@ -241,9 +258,10 @@ struct ethtool_ops {
241 int (*reset)(struct net_device *, u32 *); 258 int (*reset)(struct net_device *, u32 *);
242 u32 (*get_rxfh_key_size)(struct net_device *); 259 u32 (*get_rxfh_key_size)(struct net_device *);
243 u32 (*get_rxfh_indir_size)(struct net_device *); 260 u32 (*get_rxfh_indir_size)(struct net_device *);
244 int (*get_rxfh)(struct net_device *, u32 *indir, u8 *key); 261 int (*get_rxfh)(struct net_device *, u32 *indir, u8 *key,
262 u8 *hfunc);
245 int (*set_rxfh)(struct net_device *, const u32 *indir, 263 int (*set_rxfh)(struct net_device *, const u32 *indir,
246 const u8 *key); 264 const u8 *key, const u8 hfunc);
247 void (*get_channels)(struct net_device *, struct ethtool_channels *); 265 void (*get_channels)(struct net_device *, struct ethtool_channels *);
248 int (*set_channels)(struct net_device *, struct ethtool_channels *); 266 int (*set_channels)(struct net_device *, struct ethtool_channels *);
249 int (*get_dump_flag)(struct net_device *, struct ethtool_dump *); 267 int (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index eb2095b42fbb..5f66d9c2889d 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -534,6 +534,7 @@ struct ethtool_pauseparam {
534 * @ETH_SS_NTUPLE_FILTERS: Previously used with %ETHTOOL_GRXNTUPLE; 534 * @ETH_SS_NTUPLE_FILTERS: Previously used with %ETHTOOL_GRXNTUPLE;
535 * now deprecated 535 * now deprecated
536 * @ETH_SS_FEATURES: Device feature names 536 * @ETH_SS_FEATURES: Device feature names
537 * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names
537 */ 538 */
538enum ethtool_stringset { 539enum ethtool_stringset {
539 ETH_SS_TEST = 0, 540 ETH_SS_TEST = 0,
@@ -541,6 +542,7 @@ enum ethtool_stringset {
541 ETH_SS_PRIV_FLAGS, 542 ETH_SS_PRIV_FLAGS,
542 ETH_SS_NTUPLE_FILTERS, 543 ETH_SS_NTUPLE_FILTERS,
543 ETH_SS_FEATURES, 544 ETH_SS_FEATURES,
545 ETH_SS_RSS_HASH_FUNCS,
544}; 546};
545 547
546/** 548/**
@@ -884,6 +886,8 @@ struct ethtool_rxfh_indir {
884 * @key_size: On entry, the array size of the user buffer for the hash key, 886 * @key_size: On entry, the array size of the user buffer for the hash key,
885 * which may be zero. On return from %ETHTOOL_GRSSH, the size of the 887 * which may be zero. On return from %ETHTOOL_GRSSH, the size of the
886 * hardware hash key. 888 * hardware hash key.
889 * @hfunc: Defines the current RSS hash function used by HW (or to be set to).
890 * Valid values are one of the %ETH_RSS_HASH_*.
887 * @rsvd: Reserved for future extensions. 891 * @rsvd: Reserved for future extensions.
888 * @rss_config: RX ring/queue index for each hash value i.e., indirection table 892 * @rss_config: RX ring/queue index for each hash value i.e., indirection table
889 * of @indir_size __u32 elements, followed by hash key of @key_size 893 * of @indir_size __u32 elements, followed by hash key of @key_size
@@ -893,14 +897,16 @@ struct ethtool_rxfh_indir {
893 * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of 897 * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of
894 * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested 898 * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested
895 * and a @indir_size of zero means the indir table should be reset to default 899 * and a @indir_size of zero means the indir table should be reset to default
896 * values. 900 * values. An hfunc of zero means that hash function setting is not requested.
897 */ 901 */
898struct ethtool_rxfh { 902struct ethtool_rxfh {
899 __u32 cmd; 903 __u32 cmd;
900 __u32 rss_context; 904 __u32 rss_context;
901 __u32 indir_size; 905 __u32 indir_size;
902 __u32 key_size; 906 __u32 key_size;
903 __u32 rsvd[2]; 907 __u8 hfunc;
908 __u8 rsvd8[3];
909 __u32 rsvd32;
904 __u32 rss_config[0]; 910 __u32 rss_config[0];
905}; 911};
906#define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff 912#define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 715f51f321e9..550892cd6b3f 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -100,6 +100,12 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
100 [NETIF_F_BUSY_POLL_BIT] = "busy-poll", 100 [NETIF_F_BUSY_POLL_BIT] = "busy-poll",
101}; 101};
102 102
103static const char
104rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN] = {
105 [ETH_RSS_HASH_TOP_BIT] = "toeplitz",
106 [ETH_RSS_HASH_XOR_BIT] = "xor",
107};
108
103static int ethtool_get_features(struct net_device *dev, void __user *useraddr) 109static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
104{ 110{
105 struct ethtool_gfeatures cmd = { 111 struct ethtool_gfeatures cmd = {
@@ -185,6 +191,9 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
185 if (sset == ETH_SS_FEATURES) 191 if (sset == ETH_SS_FEATURES)
186 return ARRAY_SIZE(netdev_features_strings); 192 return ARRAY_SIZE(netdev_features_strings);
187 193
194 if (sset == ETH_SS_RSS_HASH_FUNCS)
195 return ARRAY_SIZE(rss_hash_func_strings);
196
188 if (ops->get_sset_count && ops->get_strings) 197 if (ops->get_sset_count && ops->get_strings)
189 return ops->get_sset_count(dev, sset); 198 return ops->get_sset_count(dev, sset);
190 else 199 else
@@ -199,6 +208,9 @@ static void __ethtool_get_strings(struct net_device *dev,
199 if (stringset == ETH_SS_FEATURES) 208 if (stringset == ETH_SS_FEATURES)
200 memcpy(data, netdev_features_strings, 209 memcpy(data, netdev_features_strings,
201 sizeof(netdev_features_strings)); 210 sizeof(netdev_features_strings));
211 else if (stringset == ETH_SS_RSS_HASH_FUNCS)
212 memcpy(data, rss_hash_func_strings,
213 sizeof(rss_hash_func_strings));
202 else 214 else
203 /* ops->get_strings is valid because checked earlier */ 215 /* ops->get_strings is valid because checked earlier */
204 ops->get_strings(dev, stringset, data); 216 ops->get_strings(dev, stringset, data);
@@ -618,7 +630,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
618 if (!indir) 630 if (!indir)
619 return -ENOMEM; 631 return -ENOMEM;
620 632
621 ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL); 633 ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
622 if (ret) 634 if (ret)
623 goto out; 635 goto out;
624 636
@@ -679,7 +691,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
679 goto out; 691 goto out;
680 } 692 }
681 693
682 ret = ops->set_rxfh(dev, indir, NULL); 694 ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
683 695
684out: 696out:
685 kfree(indir); 697 kfree(indir);
@@ -697,12 +709,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
697 u32 total_size; 709 u32 total_size;
698 u32 indir_bytes; 710 u32 indir_bytes;
699 u32 *indir = NULL; 711 u32 *indir = NULL;
712 u8 dev_hfunc = 0;
700 u8 *hkey = NULL; 713 u8 *hkey = NULL;
701 u8 *rss_config; 714 u8 *rss_config;
702 715
703 if (!(dev->ethtool_ops->get_rxfh_indir_size || 716 if (!ops->get_rxfh)
704 dev->ethtool_ops->get_rxfh_key_size) ||
705 !dev->ethtool_ops->get_rxfh)
706 return -EOPNOTSUPP; 717 return -EOPNOTSUPP;
707 718
708 if (ops->get_rxfh_indir_size) 719 if (ops->get_rxfh_indir_size)
@@ -710,16 +721,14 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
710 if (ops->get_rxfh_key_size) 721 if (ops->get_rxfh_key_size)
711 dev_key_size = ops->get_rxfh_key_size(dev); 722 dev_key_size = ops->get_rxfh_key_size(dev);
712 723
713 if ((dev_key_size + dev_indir_size) == 0)
714 return -EOPNOTSUPP;
715
716 if (copy_from_user(&rxfh, useraddr, sizeof(rxfh))) 724 if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
717 return -EFAULT; 725 return -EFAULT;
718 user_indir_size = rxfh.indir_size; 726 user_indir_size = rxfh.indir_size;
719 user_key_size = rxfh.key_size; 727 user_key_size = rxfh.key_size;
720 728
721 /* Check that reserved fields are 0 for now */ 729 /* Check that reserved fields are 0 for now */
722 if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1]) 730 if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] ||
731 rxfh.rsvd8[2] || rxfh.rsvd32)
723 return -EINVAL; 732 return -EINVAL;
724 733
725 rxfh.indir_size = dev_indir_size; 734 rxfh.indir_size = dev_indir_size;
@@ -727,13 +736,6 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
727 if (copy_to_user(useraddr, &rxfh, sizeof(rxfh))) 736 if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
728 return -EFAULT; 737 return -EFAULT;
729 738
730 /* If the user buffer size is 0, this is just a query for the
731 * device table size and key size. Otherwise, if the User size is
732 * not equal to device table size or key size it's an error.
733 */
734 if (!user_indir_size && !user_key_size)
735 return 0;
736
737 if ((user_indir_size && (user_indir_size != dev_indir_size)) || 739 if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
738 (user_key_size && (user_key_size != dev_key_size))) 740 (user_key_size && (user_key_size != dev_key_size)))
739 return -EINVAL; 741 return -EINVAL;
@@ -750,14 +752,19 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
750 if (user_key_size) 752 if (user_key_size)
751 hkey = rss_config + indir_bytes; 753 hkey = rss_config + indir_bytes;
752 754
753 ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey); 755 ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc);
754 if (!ret) { 756 if (ret)
755 if (copy_to_user(useraddr + 757 goto out;
756 offsetof(struct ethtool_rxfh, rss_config[0]),
757 rss_config, total_size))
758 ret = -EFAULT;
759 }
760 758
759 if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, hfunc),
760 &dev_hfunc, sizeof(rxfh.hfunc))) {
761 ret = -EFAULT;
762 } else if (copy_to_user(useraddr +
763 offsetof(struct ethtool_rxfh, rss_config[0]),
764 rss_config, total_size)) {
765 ret = -EFAULT;
766 }
767out:
761 kfree(rss_config); 768 kfree(rss_config);
762 769
763 return ret; 770 return ret;
@@ -776,33 +783,31 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
776 u8 *rss_config; 783 u8 *rss_config;
777 u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); 784 u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
778 785
779 if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) || 786 if (!ops->get_rxnfc || !ops->set_rxfh)
780 !ops->get_rxnfc || !ops->set_rxfh)
781 return -EOPNOTSUPP; 787 return -EOPNOTSUPP;
782 788
783 if (ops->get_rxfh_indir_size) 789 if (ops->get_rxfh_indir_size)
784 dev_indir_size = ops->get_rxfh_indir_size(dev); 790 dev_indir_size = ops->get_rxfh_indir_size(dev);
785 if (ops->get_rxfh_key_size) 791 if (ops->get_rxfh_key_size)
786 dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev); 792 dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev);
787 if ((dev_key_size + dev_indir_size) == 0)
788 return -EOPNOTSUPP;
789 793
790 if (copy_from_user(&rxfh, useraddr, sizeof(rxfh))) 794 if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
791 return -EFAULT; 795 return -EFAULT;
792 796
793 /* Check that reserved fields are 0 for now */ 797 /* Check that reserved fields are 0 for now */
794 if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1]) 798 if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] ||
799 rxfh.rsvd8[2] || rxfh.rsvd32)
795 return -EINVAL; 800 return -EINVAL;
796 801
797 /* If either indir or hash key is valid, proceed further. 802 /* If either indir, hash key or function is valid, proceed further.
798 * It is not valid to request that both be unchanged. 803 * Must request at least one change: indir size, hash key or function.
799 */ 804 */
800 if ((rxfh.indir_size && 805 if ((rxfh.indir_size &&
801 rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE && 806 rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
802 rxfh.indir_size != dev_indir_size) || 807 rxfh.indir_size != dev_indir_size) ||
803 (rxfh.key_size && (rxfh.key_size != dev_key_size)) || 808 (rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
804 (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE && 809 (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
805 rxfh.key_size == 0)) 810 rxfh.key_size == 0 && rxfh.hfunc == ETH_RSS_HASH_NO_CHANGE))
806 return -EINVAL; 811 return -EINVAL;
807 812
808 if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) 813 if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
@@ -845,7 +850,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
845 } 850 }
846 } 851 }
847 852
848 ret = ops->set_rxfh(dev, indir, hkey); 853 ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
849 854
850out: 855out:
851 kfree(rss_config); 856 kfree(rss_config);