These settings autoconfigure the number of RSS channels to match the number of
CPUs present.

Signed-off-by: Edward Cree <ec...@solarflare.com>
---
 drivers/net/ethernet/sfc/efx.c | 143 ++++++++++++++++++++++++++++++++---------
 1 file changed, 113 insertions(+), 30 deletions(-)

diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 0705ec86..e6fdf35 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -22,6 +22,7 @@
 #include <linux/gfp.h>
 #include <linux/aer.h>
 #include <linux/interrupt.h>
+#include <xen/xen.h>
 #include "net_driver.h"
 #include "efx.h"
 #include "nic.h"
@@ -161,16 +162,19 @@ static unsigned int tx_irq_mod_usec = 150;
  */
 static unsigned int interrupt_mode;
 
-/* This is the requested number of CPUs to use for Receive-Side Scaling (RSS),
- * i.e. the number of CPUs among which we may distribute simultaneous
- * interrupt handling.
+/* This is the requested number of CPUs to use for Receive-Side Scaling
+ * (RSS), i.e. the number of CPUs among which we may distribute
+ * simultaneous interrupt handling.  Or alternatively it may be set to
+ * "packages", "cores" or "hyperthreads" to get one receive channel per
+ * package, core or hyperthread.  The default is "cores".
  *
- * Cards without MSI-X will only target one CPU via legacy or MSI interrupt.
- * The default (0) means to assign an interrupt to each core.
+ * Systems without MSI-X will only target one CPU via legacy or MSI
+ * interrupt.
  */
-static unsigned int rss_cpus;
-module_param(rss_cpus, uint, 0444);
-MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling");
+static char *rss_cpus;
+module_param(rss_cpus, charp, 0444);
+MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling, "
+                "or 'packages', 'cores' or 'hyperthreads'");
 
 static bool phy_flash_cfg;
 module_param(phy_flash_cfg, bool, 0644);
@@ -1324,51 +1328,130 @@ void efx_set_default_rx_indir_table(struct efx_nic 
*efx)
                        ethtool_rxfh_indir_default(i, efx->rss_spread);
 }
 
-static unsigned int efx_wanted_parallelism(struct efx_nic *efx)
+/* Count the number of unique packages in the given cpumask */
+static unsigned int efx_num_packages(const cpumask_t *in)
 {
-       cpumask_var_t thread_mask;
+       cpumask_var_t core_mask;
+       unsigned int count;
+       int cpu, cpu2;
+
+       if (unlikely(!zalloc_cpumask_var(&core_mask, GFP_KERNEL))) {
+               pr_warn("sfc: RSS disabled due to allocation failure\n");
+               return 1;
+       }
+
+       count = 0;
+       for_each_cpu(cpu, in) {
+               if (!cpumask_test_cpu(cpu, core_mask)) {
+                       ++count;
+
+                       /* Treat each numa node as a seperate package */
+                       for_each_cpu(cpu2, topology_core_cpumask(cpu)) {
+                               if (cpu_to_node(cpu) == cpu_to_node(cpu2))
+                                       cpumask_set_cpu(cpu2, core_mask);
+                       }
+               }
+       }
+
+       free_cpumask_var(core_mask);
+
+       return count;
+}
+
+/* Count the number of unique cores in the given cpumask */
+static unsigned int efx_num_cores(const cpumask_t *in)
+{
+       cpumask_var_t core_mask;
        unsigned int count;
        int cpu;
 
-       if (rss_cpus) {
-               count = rss_cpus;
-       } else {
-               if (unlikely(!zalloc_cpumask_var(&thread_mask, GFP_KERNEL))) {
-                       netif_warn(efx, probe, efx->net_dev,
-                                  "RSS disabled due to allocation failure\n");
-                       return 1;
+       if (unlikely(!zalloc_cpumask_var(&core_mask, GFP_KERNEL))) {
+               pr_warn("sfc: RSS disabled due to allocation failure\n");
+               return 1;
+       }
+
+       count = 0;
+       for_each_cpu(cpu, in) {
+               if (!cpumask_test_cpu(cpu, core_mask)) {
+                       ++count;
+                       cpumask_or(core_mask, core_mask,
+                                  topology_sibling_cpumask(cpu));
                }
+       }
 
-               count = 0;
-               for_each_online_cpu(cpu) {
-                       if (!cpumask_test_cpu(cpu, thread_mask)) {
-                               ++count;
-                               cpumask_or(thread_mask, thread_mask,
-                                          topology_sibling_cpumask(cpu));
-                       }
+       free_cpumask_var(core_mask);
+       return count;
+}
+
+static unsigned int efx_wanted_parallelism(struct efx_nic *efx)
+{
+       struct net_device *net_dev = efx->net_dev;
+       unsigned int n_rxq;
+
+       if (rss_cpus == NULL) {
+               /* default to 'cores' */
+               goto cores;
+       } else if (strcmp(rss_cpus, "packages") == 0) {
+               if (xen_domain()) {
+                       netif_warn(efx, drv, net_dev,
+                                  "Unable to determine CPU topology on Xen 
reliably. Using 4 rss channels.\n");
+                       n_rxq = 4;
+               } else {
+                       netif_dbg(efx,  drv, net_dev,
+                                 "using efx_num_packages()\n");
+                       n_rxq = efx_num_packages(cpu_online_mask);
+                       /* Create two RSS queues even with a single package */
+                       if (n_rxq == 1)
+                               n_rxq = 2;
                }
+       } else if (strcmp(rss_cpus, "cores") == 0) {
+cores:
+               if (xen_domain()) {
+                       netif_warn(efx, drv, net_dev,
+                                  "Unable to determine CPU topology on Xen 
reliably. Assuming hyperthreading enabled.\n");
+                       n_rxq = max_t(int, 1, num_online_cpus() / 2);
+               } else {
+                       netif_dbg(efx, drv, net_dev,
+                                 "using efx_num_cores()\n");
+                       n_rxq = efx_num_cores(cpu_online_mask);
+               }
+       } else if (strcmp(rss_cpus, "hyperthreads") == 0) {
+               n_rxq = num_online_cpus();
+       } else if (sscanf(rss_cpus, "%u", &n_rxq) == 1 && n_rxq > 0) {
+               /* nothing to do */
+       } else {
+               netif_err(efx, drv, net_dev,
+                         "Bad value for module parameter rss_cpus='%s'\n",
+                         rss_cpus);
+               /* default to 'cores' */
+               goto cores;
+       }
 
-               free_cpumask_var(thread_mask);
+       if (n_rxq > EFX_MAX_RX_QUEUES) {
+               netif_warn(efx, drv, net_dev,
+                          "Reducing number of rss channels from %u to %u.\n",
+                          n_rxq, EFX_MAX_RX_QUEUES);
+               n_rxq = EFX_MAX_RX_QUEUES;
        }
 
+#ifdef CONFIG_SFC_SRIOV
        /* If RSS is requested for the PF *and* VFs then we can't write RSS
         * table entries that are inaccessible to VFs
         */
-#ifdef CONFIG_SFC_SRIOV
        if (efx->type->sriov_wanted) {
                if (efx->type->sriov_wanted(efx) && efx_vf_size(efx) > 1 &&
-                   count > efx_vf_size(efx)) {
+                   n_rxq > efx_vf_size(efx)) {
                        netif_warn(efx, probe, efx->net_dev,
                                   "Reducing number of RSS channels from %u to 
%u for "
                                   "VF support. Increase vf-msix-limit to use 
more "
                                   "channels on the PF.\n",
-                                  count, efx_vf_size(efx));
-                       count = efx_vf_size(efx);
+                                  n_rxq, efx_vf_size(efx));
+                       n_rxq = efx_vf_size(efx);
                }
        }
 #endif
 
-       return count;
+       return n_rxq;
 }
 
 /* Probe the number and type of interrupts we are able to obtain, and

Reply via email to