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 /net/core | |
| 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>
Diffstat (limited to 'net/core')
| -rw-r--r-- | net/core/ethtool.c | 69 |
1 files changed, 37 insertions, 32 deletions
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); |
