tg3: Make the RSS indir tbl admin configurable
authorMatt Carlson <mcarlson@broadcom.com>
Fri, 16 Dec 2011 13:33:23 +0000 (13:33 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 19 Dec 2011 20:50:52 +0000 (15:50 -0500)
This patch adds the ethtool callbacks necessary to change the rss
indirection table from userspace.  Should the number of interrupts
change (e.g.  across a close / open call, or through a reset) and
any one of the indirection table values fall out-of-range, the driver
will reset the indirection table to a default layout.

[Integrated many suggestions made by Ben Hutchings.]

Changes since v3

* Removed TG3_FLAG_SUPPORT_MSIX checks at the start of
  tg3_get_rxfh_indir() and tg3_set_rxfh_indir().

Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Signed-off-by: Michael Chan <mchan@broadcom.com>
Reviewed-by: Benjamin Li <benli@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/tg3.c

index 8bf11ca30efecd888630765529c91b28ab5e8318..00213968d21009cc1748899c35c50c869875b687 100644 (file)
@@ -8220,21 +8220,38 @@ static void tg3_setup_rxbd_thresholds(struct tg3 *tp)
                tw32(JMB_REPLENISH_LWM, bdcache_maxcnt);
 }
 
-void tg3_rss_init_indir_tbl(struct tg3 *tp)
+static void tg3_rss_init_dflt_indir_tbl(struct tg3 *tp)
+{
+       int i;
+
+       for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++)
+               tp->rss_ind_tbl[i] =
+                       ethtool_rxfh_indir_default(i, tp->irq_cnt - 1);
+}
+
+static void tg3_rss_check_indir_tbl(struct tg3 *tp)
 {
        int i;
 
        if (!tg3_flag(tp, SUPPORT_MSIX))
                return;
 
-       if (tp->irq_cnt <= 2)
+       if (tp->irq_cnt <= 2) {
                memset(&tp->rss_ind_tbl[0], 0, sizeof(tp->rss_ind_tbl));
-       else
-               for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++)
-                       tp->rss_ind_tbl[i] = i % (tp->irq_cnt - 1);
+               return;
+       }
+
+       /* Validate table against current IRQ count */
+       for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++) {
+               if (tp->rss_ind_tbl[i] >= tp->irq_cnt - 1)
+                       break;
+       }
+
+       if (i != TG3_RSS_INDIR_TBL_SIZE)
+               tg3_rss_init_dflt_indir_tbl(tp);
 }
 
-void tg3_rss_write_indir_tbl(struct tg3 *tp)
+static void tg3_rss_write_indir_tbl(struct tg3 *tp)
 {
        int i = 0;
        u32 reg = MAC_RSS_INDIR_TBL_0;
@@ -9668,7 +9685,7 @@ static int tg3_open(struct net_device *dev)
         */
        tg3_ints_init(tp);
 
-       tg3_rss_init_indir_tbl(tp);
+       tg3_rss_check_indir_tbl(tp);
 
        /* The placement of this call is tied
         * to the setup and use of Host TX descriptors.
@@ -10719,6 +10736,78 @@ static int tg3_get_sset_count(struct net_device *dev, int sset)
        }
 }
 
+static int tg3_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
+                        u32 *rules __always_unused)
+{
+       struct tg3 *tp = netdev_priv(dev);
+
+       if (!tg3_flag(tp, SUPPORT_MSIX))
+               return -EOPNOTSUPP;
+
+       switch (info->cmd) {
+       case ETHTOOL_GRXRINGS:
+               if (netif_running(tp->dev))
+                       info->data = tp->irq_cnt;
+               else {
+                       info->data = num_online_cpus();
+                       if (info->data > TG3_IRQ_MAX_VECS_RSS)
+                               info->data = TG3_IRQ_MAX_VECS_RSS;
+               }
+
+               /* The first interrupt vector only
+                * handles link interrupts.
+                */
+               info->data -= 1;
+               return 0;
+
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static u32 tg3_get_rxfh_indir_size(struct net_device *dev)
+{
+       u32 size = 0;
+       struct tg3 *tp = netdev_priv(dev);
+
+       if (tg3_flag(tp, SUPPORT_MSIX))
+               size = TG3_RSS_INDIR_TBL_SIZE;
+
+       return size;
+}
+
+static int tg3_get_rxfh_indir(struct net_device *dev, u32 *indir)
+{
+       struct tg3 *tp = netdev_priv(dev);
+       int i;
+
+       for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++)
+               indir[i] = tp->rss_ind_tbl[i];
+
+       return 0;
+}
+
+static int tg3_set_rxfh_indir(struct net_device *dev, const u32 *indir)
+{
+       struct tg3 *tp = netdev_priv(dev);
+       size_t i;
+
+       for (i = 0; i < TG3_RSS_INDIR_TBL_SIZE; i++)
+               tp->rss_ind_tbl[i] = indir[i];
+
+       if (!netif_running(dev) || !tg3_flag(tp, ENABLE_RSS))
+               return 0;
+
+       /* It is legal to write the indirection
+        * table while the device is running.
+        */
+       tg3_full_lock(tp, 0);
+       tg3_rss_write_indir_tbl(tp);
+       tg3_full_unlock(tp);
+
+       return 0;
+}
+
 static void tg3_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
 {
        switch (stringset) {
@@ -11949,6 +12038,10 @@ static const struct ethtool_ops tg3_ethtool_ops = {
        .get_coalesce           = tg3_get_coalesce,
        .set_coalesce           = tg3_set_coalesce,
        .get_sset_count         = tg3_get_sset_count,
+       .get_rxnfc              = tg3_get_rxnfc,
+       .get_rxfh_indir_size    = tg3_get_rxfh_indir_size,
+       .get_rxfh_indir         = tg3_get_rxfh_indir,
+       .set_rxfh_indir         = tg3_set_rxfh_indir,
 };
 
 static void __devinit tg3_get_eeprom_size(struct tg3 *tp)
@@ -14051,6 +14144,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
                if (tg3_flag(tp, 57765_PLUS)) {
                        tg3_flag_set(tp, SUPPORT_MSIX);
                        tp->irq_max = TG3_IRQ_MAX_VECS;
+                       tg3_rss_init_dflt_indir_tbl(tp);
                }
        }
 
This page took 0.050872 seconds and 5 git commands to generate.