diff options
author | Eyal Perry <eyalpe@mellanox.com> | 2014-12-02 11:12:10 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-12-08 21:07:10 -0500 |
commit | 892311f66f2411b813ca631009356891a0c2b0a1 (patch) | |
tree | 42267098964290cf349abd6733267c24867f2318 | |
parent | 18b5427ae1654803930b97590ac5a85245861646 (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.c | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 20 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/tg3.c | 20 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 18 | ||||
-rw-r--r-- | drivers/net/ethernet/emulex/benet/be_ethtool.c | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 17 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igb/igb_ethtool.c | 16 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ethtool.c | 18 | ||||
-rw-r--r-- | drivers/net/vmxnet3/vmxnet3_ethtool.c | 15 | ||||
-rw-r--r-- | include/linux/ethtool.h | 42 | ||||
-rw-r--r-- | include/uapi/linux/ethtool.h | 10 | ||||
-rw-r--r-- | net/core/ethtool.c | 69 |
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 | ||
514 | static int xgbe_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) | 514 | static 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 | ||
531 | static int xgbe_set_rxfh(struct net_device *netdev, const u32 *indir, | 535 | static 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 | ||
3361 | static int bnx2x_get_rxfh(struct net_device *dev, u32 *indir, u8 *key) | 3361 | static 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 | ||
3385 | static int bnx2x_set_rxfh(struct net_device *dev, const u32 *indir, | 3391 | static 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 | ||
12564 | static int tg3_get_rxfh(struct net_device *dev, u32 *indir, u8 *key) | 12564 | static 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 | ||
12575 | static int tg3_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key) | 12580 | static 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 | ||
2926 | static int get_rss_table(struct net_device *dev, u32 *p, u8 *key) | 2926 | static 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 | ||
2936 | static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key) | 2940 | static 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 | ||
1174 | static int be_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey) | 1174 | static 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 | ||
1191 | static int be_set_rxfh(struct net_device *netdev, const u32 *indir, | 1195 | static 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 | ||
919 | static int fm10k_get_rssh(struct net_device *netdev, u32 *indir, u8 *key) | 919 | static 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 | ||
934 | static int fm10k_set_rssh(struct net_device *netdev, const u32 *indir, | 938 | static 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 | **/ |
630 | static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) | 630 | static 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 | **/ |
656 | static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, | 662 | static 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 | ||
2845 | static int igb_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) | 2845 | static 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 | ||
2891 | static int igb_set_rxfh(struct net_device *netdev, const u32 *indir, | 2896 | static 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 | ||
981 | static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key) | 981 | static 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 | ||
1001 | static int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index, | 1006 | static 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 | ||
1089 | static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key) | 1089 | static 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 | ||
1097 | static int efx_ethtool_set_rxfh(struct net_device *net_dev, | 1101 | static 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 | ||
585 | static int | 585 | static int |
586 | vmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key) | 586 | vmxnet3_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 | ||
598 | static int | 602 | static int |
599 | vmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key) | 603 | vmxnet3_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 | ||
62 | enum { | ||
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 | |||
62 | struct net_device; | 82 | struct 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 | */ |
538 | enum ethtool_stringset { | 539 | enum 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 | */ |
898 | struct ethtool_rxfh { | 902 | struct 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 | ||
103 | static const char | ||
104 | rss_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 | |||
103 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) | 109 | static 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 | ||
684 | out: | 696 | out: |
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 | } | ||
767 | out: | ||
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 | ||
850 | out: | 855 | out: |
851 | kfree(rss_config); | 856 | kfree(rss_config); |