igb: Add ethtool support to configure number of channels
authorLaura Mihaela Vasilescu <laura.vasilescu@rosedu.org>
Tue, 1 Oct 2013 11:33:56 +0000 (04:33 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 1 Oct 2013 16:49:49 +0000 (12:49 -0400)
This patch adds the ethtool callbacks necessary to configure the
number of RSS queues.

The maximum number of queues is in accordance with the datasheets.

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>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c

index cdaa2bc..5e9ed89 100644 (file)
@@ -487,6 +487,7 @@ int igb_up(struct igb_adapter *);
 void igb_down(struct igb_adapter *);
 void igb_reinit_locked(struct igb_adapter *);
 void igb_reset(struct igb_adapter *);
+int igb_reinit_queues(struct igb_adapter *);
 void igb_write_rss_indir_tbl(struct igb_adapter *);
 int igb_set_spd_dplx(struct igb_adapter *, u32, u8);
 int igb_setup_tx_resources(struct igb_ring *);
index e4c77c0..c8f65e5 100644 (file)
@@ -2874,6 +2874,88 @@ static int igb_set_rxfh_indir(struct net_device *netdev, const u32 *indir)
        return 0;
 }
 
+static unsigned int igb_max_channels(struct igb_adapter *adapter)
+{
+       struct e1000_hw *hw = &adapter->hw;
+       unsigned int max_combined = 0;
+
+       switch (hw->mac.type) {
+       case e1000_i211:
+               max_combined = IGB_MAX_RX_QUEUES_I211;
+               break;
+       case e1000_82575:
+       case e1000_i210:
+               max_combined = IGB_MAX_RX_QUEUES_82575;
+               break;
+       case e1000_i350:
+               if (!!adapter->vfs_allocated_count) {
+                       max_combined = 1;
+                       break;
+               }
+               /* fall through */
+       case e1000_82576:
+               if (!!adapter->vfs_allocated_count) {
+                       max_combined = 2;
+                       break;
+               }
+               /* fall through */
+       case e1000_82580:
+       case e1000_i354:
+       default:
+               max_combined = IGB_MAX_RX_QUEUES;
+               break;
+       }
+
+       return max_combined;
+}
+
+static void igb_get_channels(struct net_device *netdev,
+                            struct ethtool_channels *ch)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+
+       /* Report maximum channels */
+       ch->max_combined = igb_max_channels(adapter);
+
+       /* Report info for other vector */
+       if (adapter->msix_entries) {
+               ch->max_other = NON_Q_VECTORS;
+               ch->other_count = NON_Q_VECTORS;
+       }
+
+       ch->combined_count = adapter->rss_queues;
+}
+
+static int igb_set_channels(struct net_device *netdev,
+                           struct ethtool_channels *ch)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       unsigned int count = ch->combined_count;
+
+       /* Verify they are not requesting separate vectors */
+       if (!count || ch->rx_count || ch->tx_count)
+               return -EINVAL;
+
+       /* Verify other_count is valid and has not been changed */
+       if (ch->other_count != NON_Q_VECTORS)
+               return -EINVAL;
+
+       /* Verify the number of channels doesn't exceed hw limits */
+       if (count > igb_max_channels(adapter))
+               return -EINVAL;
+
+       if (count != adapter->rss_queues) {
+               adapter->rss_queues = count;
+
+               /* Hardware has to reinitialize queues and interrupts to
+                * match the new configuration.
+                */
+               return igb_reinit_queues(adapter);
+       }
+
+       return 0;
+}
+
 static const struct ethtool_ops igb_ethtool_ops = {
        .get_settings           = igb_get_settings,
        .set_settings           = igb_set_settings,
@@ -2910,6 +2992,8 @@ static const struct ethtool_ops igb_ethtool_ops = {
        .get_rxfh_indir_size    = igb_get_rxfh_indir_size,
        .get_rxfh_indir         = igb_get_rxfh_indir,
        .set_rxfh_indir         = igb_set_rxfh_indir,
+       .get_channels           = igb_get_channels,
+       .set_channels           = igb_set_channels,
        .begin                  = igb_ethtool_begin,
        .complete               = igb_ethtool_complete,
 };
index 8cf44f2..a56266e 100644 (file)
@@ -7838,4 +7838,26 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
                return E1000_SUCCESS;
 
 }
+
+int igb_reinit_queues(struct igb_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       struct pci_dev *pdev = adapter->pdev;
+       int err = 0;
+
+       if (netif_running(netdev))
+               igb_close(netdev);
+
+       igb_clear_interrupt_scheme(adapter);
+
+       if (igb_init_interrupt_scheme(adapter, true)) {
+               dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
+               return -ENOMEM;
+       }
+
+       if (netif_running(netdev))
+               err = igb_open(netdev);
+
+       return err;
+}
 /* igb_main.c */