// SPDX-License-Identifier: GPL-2.0-only
/****************************************************************************
* Driver for Solarflare network controllers and boards
* Copyright 2018 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
#include "net_driver.h"
#include <linux/module.h>
#include <linux/filter.h>
#include "efx_channels.h"
#include "efx.h"
#include "efx_common.h"
#include "tx_common.h"
#include "rx_common.h"
#include "nic.h"
#include "sriov.h"
#include "workarounds.h"
/* This is the first interrupt mode to try out of:
* 0 => MSI-X
* 1 => MSI
* 2 => legacy
*/
unsigned int efx_interrupt_mode = EFX_INT_MODE_MSIX;
/* 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.
*
* 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.
*/
unsigned int rss_cpus;
static unsigned int irq_adapt_low_thresh = 8000;
module_param(irq_adapt_low_thresh, uint, 0644);
MODULE_PARM_DESC(irq_adapt_low_thresh,
"Threshold score for reducing IRQ moderation");
static unsigned int irq_adapt_high_thresh = 16000;
module_param(irq_adapt_high_thresh, uint, 0644);
MODULE_PARM_DESC(irq_adapt_high_thresh,
"Threshold score for increasing IRQ moderation");
static const struct efx_channel_type efx_default_channel_type;
/*************
* INTERRUPTS
*************/
static unsigned int count_online_cores(struct efx_nic *efx, bool local_node)
{
cpumask_var_t filter_mask;
unsigned int count;
int cpu;
if (unlikely(!zalloc_cpumask_var(&filter_mask, GFP_KERNEL))) {
netif_warn(efx, probe, efx->net_dev,
"RSS disabled due to allocation failure\n");
return 1;
}
cpumask_copy(filter_mask, cpu_online_mask);
if (local_node)
cpumask_and(filter_mask, filter_mask,
cpumask_of_pcibus(efx->pci_dev->bus));
count = 0;
for_each_cpu(cpu, filter_mask) {
++count;
cpumask_andnot(filter_mask, filter_mask, topology_sibling_cpumask(cpu));
}
free_cpumask_var(filter_mask);
return count;
}
static unsigned int efx_wanted_parallelism(struct efx_nic *efx)
{
unsigned int count;
if (rss_cpus) {
count = rss_cpus;
} else {
count = count_online_cores(efx, true);
/* If no online CPUs in local node, fallback to any online CPUs */
if (count == 0)
count = count_online_cores(efx, false);
}
if (count > EFX_MAX_RX_QUEUES) {
netif_cond_dbg(efx, probe, efx->net_dev, !rss_cpus, warn,
"Reducing number of rx queues from %u to %u.\n",
count