igb: Expose RSS indirection table for ethtool
authorLaura Mihaela Vasilescu <laura.vasilescu@rosedu.org>
Wed, 31 Jul 2013 20:19:54 +0000 (20:19 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Thu, 22 Aug 2013 09:26:27 +0000 (02:26 -0700)
This patch adds the ethtool callbacks necessary to change the RETA
indirection table from userspace.

In order to achieve this, we add the indirection table field (rss_indir_tbl)
in the board specific data structure (struct igb_adapter) to preserve the
values across hardware resets.

The indirection table must be initialized with default values in the
following cases:
* at module init time
* when the number of RX queues changes.
For this reason we add a new field (rss_indir_tbl_init) in igb_adapter
that keeps track of the number of RX queues. Whenever the number of RX
queues changes, the rss_indir_tbl is modified and initialized with default
values. The rss_indir_tbl_init is updated accordingly.

CC: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: Laura Mihaela Vasilescu <laura.vasilescu@rosedu.org>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c

index 5a2659bf0c322925fcbd32cf34e995365dbe38c4..c1fae7aa0bd55b278205f493701337b2f9eaac4b 100644 (file)
@@ -446,6 +446,8 @@ struct igb_adapter {
        struct i2c_algo_bit_data i2c_algo;
        struct i2c_adapter i2c_adap;
        struct i2c_client *i2c_client;
+       u32 rss_indir_tbl_init;
+       u8 rss_indir_tbl[IGB_RETA_SIZE];
 };
 
 #define IGB_FLAG_HAS_MSI               (1 << 0)
@@ -482,6 +484,7 @@ extern int igb_up(struct igb_adapter *);
 extern void igb_down(struct igb_adapter *);
 extern void igb_reinit_locked(struct igb_adapter *);
 extern void igb_reset(struct igb_adapter *);
+extern void igb_write_rss_indir_tbl(struct igb_adapter *);
 extern int igb_set_spd_dplx(struct igb_adapter *, u32, u8);
 extern int igb_setup_tx_resources(struct igb_ring *);
 extern int igb_setup_rx_resources(struct igb_ring *);
index 03137e21551dff68644c66ab5309cfb0324b1ceb..ce9b5a9e480cd953086a6986d841e72a330ef7f6 100644 (file)
@@ -2784,6 +2784,90 @@ static void igb_ethtool_complete(struct net_device *netdev)
        pm_runtime_put(&adapter->pdev->dev);
 }
 
+static u32 igb_get_rxfh_indir_size(struct net_device *netdev)
+{
+       return IGB_RETA_SIZE;
+}
+
+static int igb_get_rxfh_indir(struct net_device *netdev, u32 *indir)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       int i;
+
+       for (i = 0; i < IGB_RETA_SIZE; i++)
+               indir[i] = adapter->rss_indir_tbl[i];
+
+       return 0;
+}
+
+void igb_write_rss_indir_tbl(struct igb_adapter *adapter)
+{
+       struct e1000_hw *hw = &adapter->hw;
+       u32 reg = E1000_RETA(0);
+       u32 shift = 0;
+       int i = 0;
+
+       switch (hw->mac.type) {
+       case e1000_82575:
+               shift = 6;
+               break;
+       case e1000_82576:
+               /* 82576 supports 2 RSS queues for SR-IOV */
+               if (adapter->vfs_allocated_count)
+                       shift = 3;
+               break;
+       default:
+               break;
+       }
+
+       while (i < IGB_RETA_SIZE) {
+               u32 val = 0;
+               int j;
+
+               for (j = 3; j >= 0; j--) {
+                       val <<= 8;
+                       val |= adapter->rss_indir_tbl[i + j];
+               }
+
+               wr32(reg, val << shift);
+               reg += 4;
+               i += 4;
+       }
+}
+
+static int igb_set_rxfh_indir(struct net_device *netdev, const u32 *indir)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       struct e1000_hw *hw = &adapter->hw;
+       int i;
+       u32 num_queues;
+
+       num_queues = adapter->rss_queues;
+
+       switch (hw->mac.type) {
+       case e1000_82576:
+               /* 82576 supports 2 RSS queues for SR-IOV */
+               if (adapter->vfs_allocated_count)
+                       num_queues = 2;
+               break;
+       default:
+               break;
+       }
+
+       /* Verify user input. */
+       for (i = 0; i < IGB_RETA_SIZE; i++)
+               if (indir[i] >= num_queues)
+                       return -EINVAL;
+
+
+       for (i = 0; i < IGB_RETA_SIZE; i++)
+               adapter->rss_indir_tbl[i] = indir[i];
+
+       igb_write_rss_indir_tbl(adapter);
+
+       return 0;
+}
+
 static const struct ethtool_ops igb_ethtool_ops = {
        .get_settings           = igb_get_settings,
        .set_settings           = igb_set_settings,
@@ -2817,6 +2901,9 @@ static const struct ethtool_ops igb_ethtool_ops = {
        .set_eee                = igb_set_eee,
        .get_module_info        = igb_get_module_info,
        .get_module_eeprom      = igb_get_module_eeprom,
+       .get_rxfh_indir_size    = igb_get_rxfh_indir_size,
+       .get_rxfh_indir         = igb_get_rxfh_indir,
+       .set_rxfh_indir         = igb_set_rxfh_indir,
        .begin                  = igb_ethtool_begin,
        .complete               = igb_ethtool_complete,
 };
index 1acd9c06026af17236f638d75a165a4937679d64..df33c4b8fa8198846a024df90b70b37afa9c2260 100644 (file)
@@ -3126,7 +3126,7 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 mrqc, rxcsum;
-       u32 j, num_rx_queues, shift = 0;
+       u32 j, num_rx_queues;
        static const u32 rsskey[10] = { 0xDA565A6D, 0xC20E5B25, 0x3D256741,
                                        0xB08FA343, 0xCB2BCAD0, 0xB4307BAE,
                                        0xA32DCB77, 0x0CF23080, 0x3BB7426A,
@@ -3139,35 +3139,21 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
        num_rx_queues = adapter->rss_queues;
 
        switch (hw->mac.type) {
-       case e1000_82575:
-               shift = 6;
-               break;
        case e1000_82576:
                /* 82576 supports 2 RSS queues for SR-IOV */
-               if (adapter->vfs_allocated_count) {
-                       shift = 3;
+               if (adapter->vfs_allocated_count)
                        num_rx_queues = 2;
-               }
                break;
        default:
                break;
        }
 
-       /* Populate the indirection table 4 entries at a time.  To do this
-        * we are generating the results for n and n+2 and then interleaving
-        * those with the results with n+1 and n+3.
-        */
-       for (j = 0; j < IGB_RETA_SIZE / 4; j++) {
-               /* first pass generates n and n+2 */
-               u32 base = ((j * 0x00040004) + 0x00020000) * num_rx_queues;
-               u32 reta = (base & 0x07800780) >> (7 - shift);
-
-               /* second pass generates n+1 and n+3 */
-               base += 0x00010001 * num_rx_queues;
-               reta |= (base & 0x07800780) << (1 + shift);
-
-               wr32(E1000_RETA(j), reta);
+       if (adapter->rss_indir_tbl_init != num_rx_queues) {
+               for (j = 0; j < IGB_RETA_SIZE; j++)
+                       adapter->rss_indir_tbl[j] = (j * num_rx_queues) / IGB_RETA_SIZE;
+               adapter->rss_indir_tbl_init = num_rx_queues;
        }
+       igb_write_rss_indir_tbl(adapter);
 
        /* Disable raw packet checksumming so that RSS hash is placed in
         * descriptor on writeback.  No need to enable TCP/UDP/IP checksum