nfp: add support for reporting CRC32 hash function
authorJakub Kicinski <jakub.kicinski@netronome.com>
Wed, 8 Mar 2017 16:57:01 +0000 (08:57 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 10 Mar 2017 00:39:58 +0000 (16:39 -0800)
Some firmware images may reuse CRC32 hardware to compute RXHASH.
Make sure we report the correct hash function.  Note that we don't
support changing functions at runtime.  That would also require
a few more additions to the way the key is set because different
functions have different key sizes.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/nfp_net.h
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c

index e614a37..9843e95 100644 (file)
@@ -446,6 +446,7 @@ struct nfp_stat_pair {
  * @fw_ver:             Firmware version
  * @cap:                Capabilities advertised by the Firmware
  * @max_mtu:            Maximum support MTU advertised by the Firmware
+ * @rss_hfunc:         RSS selected hash function
  * @rss_cfg:            RSS configuration
  * @rss_key:            RSS secret key
  * @rss_itbl:           RSS indirection table
@@ -518,6 +519,7 @@ struct nfp_net {
        u32 cap;
        u32 max_mtu;
 
+       u8 rss_hfunc;
        u32 rss_cfg;
        u8 rss_key[NFP_NET_CFG_RSS_KEY_SZ];
        u8 rss_itbl[NFP_NET_CFG_RSS_ITBL_SZ];
@@ -776,6 +778,7 @@ void nfp_net_netdev_clean(struct net_device *netdev);
 void nfp_net_set_ethtool_ops(struct net_device *netdev);
 void nfp_net_info(struct nfp_net *nn);
 int nfp_net_reconfig(struct nfp_net *nn, u32 update);
+unsigned int nfp_net_rss_key_sz(struct nfp_net *nn);
 void nfp_net_rss_write_itbl(struct nfp_net *nn);
 void nfp_net_rss_write_key(struct nfp_net *nn);
 void nfp_net_coalesce_write_cfg(struct nfp_net *nn);
index 9179a99..e72468d 100644 (file)
@@ -41,6 +41,7 @@
  *          Chris Telfer <chris.telfer@netronome.com>
  */
 
+#include <linux/bitfield.h>
 #include <linux/bpf.h>
 #include <linux/bpf_trace.h>
 #include <linux/module.h>
@@ -2045,7 +2046,7 @@ void nfp_net_rss_write_key(struct nfp_net *nn)
 {
        int i;
 
-       for (i = 0; i < NFP_NET_CFG_RSS_KEY_SZ; i += 4)
+       for (i = 0; i < nfp_net_rss_key_sz(nn); i += 4)
                nn_writel(nn, NFP_NET_CFG_RSS_KEY + i,
                          get_unaligned_le32(nn->rss_key + i));
 }
@@ -3111,20 +3112,59 @@ void nfp_net_netdev_free(struct nfp_net *nn)
        free_netdev(nn->netdev);
 }
 
+/**
+ * nfp_net_rss_key_sz() - Get current size of the RSS key
+ * @nn:                NFP Net device instance
+ *
+ * Return: size of the RSS key for currently selected hash function.
+ */
+unsigned int nfp_net_rss_key_sz(struct nfp_net *nn)
+{
+       switch (nn->rss_hfunc) {
+       case ETH_RSS_HASH_TOP:
+               return NFP_NET_CFG_RSS_KEY_SZ;
+       case ETH_RSS_HASH_XOR:
+               return 0;
+       case ETH_RSS_HASH_CRC32:
+               return 4;
+       }
+
+       nn_warn(nn, "Unknown hash function: %u\n", nn->rss_hfunc);
+       return 0;
+}
+
 /**
  * nfp_net_rss_init() - Set the initial RSS parameters
  * @nn:             NFP Net device to reconfigure
  */
 static void nfp_net_rss_init(struct nfp_net *nn)
 {
-       netdev_rss_key_fill(nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ);
+       unsigned long func_bit, rss_cap_hfunc;
+       u32 reg;
+
+       /* Read the RSS function capability and select first supported func */
+       reg = nn_readl(nn, NFP_NET_CFG_RSS_CAP);
+       rss_cap_hfunc = FIELD_GET(NFP_NET_CFG_RSS_CAP_HFUNC, reg);
+       if (!rss_cap_hfunc)
+               rss_cap_hfunc = FIELD_GET(NFP_NET_CFG_RSS_CAP_HFUNC,
+                                         NFP_NET_CFG_RSS_TOEPLITZ);
+
+       func_bit = find_first_bit(&rss_cap_hfunc, NFP_NET_CFG_RSS_HFUNCS);
+       if (func_bit == NFP_NET_CFG_RSS_HFUNCS) {
+               dev_warn(&nn->pdev->dev,
+                        "Bad RSS config, defaulting to Toeplitz hash\n");
+               func_bit = ETH_RSS_HASH_TOP_BIT;
+       }
+       nn->rss_hfunc = 1 << func_bit;
+
+       netdev_rss_key_fill(nn->rss_key, nfp_net_rss_key_sz(nn));
 
        nfp_net_rss_init_itbl(nn);
 
        /* Enable IPv4/IPv6 TCP by default */
        nn->rss_cfg = NFP_NET_CFG_RSS_IPV4_TCP |
                      NFP_NET_CFG_RSS_IPV6_TCP |
-                     NFP_NET_CFG_RSS_TOEPLITZ |
+                     FIELD_PREP(NFP_NET_CFG_RSS_HFUNC, nn->rss_hfunc) |
                      NFP_NET_CFG_RSS_MASK;
 }
 
index 385ba35..71d8617 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Netronome Systems, Inc.
+ * Copyright (C) 2015-2017 Netronome Systems, Inc.
  *
  * This software is dual licensed under the GNU General License Version 2,
  * June 1991 as shown in the file COPYING in the top-level directory of this
 #define NFP_NET_CFG_RX_OFFSET          0x0050
 #define NFP_NET_CFG_RX_OFFSET_DYNAMIC          0       /* Prepend mode */
 
+/**
+ * RSS capabilities
+ * @NFP_NET_CFG_RSS_CAP_HFUNC: supported hash functions (same bits as
+ *                             @NFP_NET_CFG_RSS_HFUNC)
+ */
+#define NFP_NET_CFG_RSS_CAP            0x0054
+#define   NFP_NET_CFG_RSS_CAP_HFUNC      0xff000000
+
 /**
  * VXLAN/UDP encap configuration
  * @NFP_NET_CFG_VXLAN_PORT:    Base address of table of tunnels' UDP dst ports
 #define   NFP_NET_CFG_RSS_IPV4_UDP        (1 << 11) /* RSS for IPv4/UDP */
 #define   NFP_NET_CFG_RSS_IPV6_TCP        (1 << 12) /* RSS for IPv6/TCP */
 #define   NFP_NET_CFG_RSS_IPV6_UDP        (1 << 13) /* RSS for IPv6/UDP */
+#define   NFP_NET_CFG_RSS_HFUNC                  0xff000000
 #define   NFP_NET_CFG_RSS_TOEPLITZ        (1 << 24) /* Use Toeplitz hash */
+#define   NFP_NET_CFG_RSS_XOR            (1 << 25) /* Use XOR as hash */
+#define   NFP_NET_CFG_RSS_CRC32                  (1 << 26) /* Use CRC32 as hash */
+#define   NFP_NET_CFG_RSS_HFUNCS         3
 #define NFP_NET_CFG_RSS_KEY             (NFP_NET_CFG_RSS_BASE + 0x4)
 #define NFP_NET_CFG_RSS_KEY_SZ          0x28
 #define NFP_NET_CFG_RSS_ITBL            (NFP_NET_CFG_RSS_BASE + 0x4 + \
index 2649f75..a1bca2d 100644 (file)
@@ -40,6 +40,7 @@
  *          Brad Petrus <brad.petrus@netronome.com>
  */
 
+#include <linux/bitfield.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -454,7 +455,7 @@ static int nfp_net_set_rss_hash_opt(struct nfp_net *nn,
                return -EINVAL;
        }
 
-       new_rss_cfg |= NFP_NET_CFG_RSS_TOEPLITZ;
+       new_rss_cfg |= FIELD_PREP(NFP_NET_CFG_RSS_HFUNC, nn->rss_hfunc);
        new_rss_cfg |= NFP_NET_CFG_RSS_MASK;
 
        if (new_rss_cfg == nn->rss_cfg)
@@ -496,7 +497,12 @@ static u32 nfp_net_get_rxfh_indir_size(struct net_device *netdev)
 
 static u32 nfp_net_get_rxfh_key_size(struct net_device *netdev)
 {
-       return NFP_NET_CFG_RSS_KEY_SZ;
+       struct nfp_net *nn = netdev_priv(netdev);
+
+       if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+               return -EOPNOTSUPP;
+
+       return nfp_net_rss_key_sz(nn);
 }
 
 static int nfp_net_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
@@ -512,9 +518,12 @@ static int nfp_net_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
                for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++)
                        indir[i] = nn->rss_itbl[i];
        if (key)
-               memcpy(key, nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ);
-       if (hfunc)
-               *hfunc = ETH_RSS_HASH_TOP;
+               memcpy(key, nn->rss_key, nfp_net_rss_key_sz(nn));
+       if (hfunc) {
+               *hfunc = nn->rss_hfunc;
+               if (*hfunc >= 1 << ETH_RSS_HASH_FUNCS_COUNT)
+                       *hfunc = ETH_RSS_HASH_UNKNOWN;
+       }
 
        return 0;
 }
@@ -527,14 +536,14 @@ static int nfp_net_set_rxfh(struct net_device *netdev,
        int i;
 
        if (!(nn->cap & NFP_NET_CFG_CTRL_RSS) ||
-           !(hfunc == ETH_RSS_HASH_NO_CHANGE || hfunc == ETH_RSS_HASH_TOP))
+           !(hfunc == ETH_RSS_HASH_NO_CHANGE || hfunc == nn->rss_hfunc))
                return -EOPNOTSUPP;
 
        if (!key && !indir)
                return 0;
 
        if (key) {
-               memcpy(nn->rss_key, key, NFP_NET_CFG_RSS_KEY_SZ);
+               memcpy(nn->rss_key, key, nfp_net_rss_key_sz(nn));
                nfp_net_rss_write_key(nn);
        }
        if (indir) {