162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/**************************************************************************** 362306a36Sopenharmony_ci * Driver for Solarflare network controllers and boards 462306a36Sopenharmony_ci * Copyright 2018 Solarflare Communications Inc. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 762306a36Sopenharmony_ci * under the terms of the GNU General Public License version 2 as published 862306a36Sopenharmony_ci * by the Free Software Foundation, incorporated herein by reference. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "net_driver.h" 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/filter.h> 1462306a36Sopenharmony_ci#include "efx_channels.h" 1562306a36Sopenharmony_ci#include "efx.h" 1662306a36Sopenharmony_ci#include "efx_common.h" 1762306a36Sopenharmony_ci#include "tx_common.h" 1862306a36Sopenharmony_ci#include "rx_common.h" 1962306a36Sopenharmony_ci#include "nic.h" 2062306a36Sopenharmony_ci#include "sriov.h" 2162306a36Sopenharmony_ci#include "workarounds.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* This is the first interrupt mode to try out of: 2462306a36Sopenharmony_ci * 0 => MSI-X 2562306a36Sopenharmony_ci * 1 => MSI 2662306a36Sopenharmony_ci * 2 => legacy 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ciunsigned int efx_siena_interrupt_mode = EFX_INT_MODE_MSIX; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* This is the requested number of CPUs to use for Receive-Side Scaling (RSS), 3162306a36Sopenharmony_ci * i.e. the number of CPUs among which we may distribute simultaneous 3262306a36Sopenharmony_ci * interrupt handling. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Cards without MSI-X will only target one CPU via legacy or MSI interrupt. 3562306a36Sopenharmony_ci * The default (0) means to assign an interrupt to each core. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ciunsigned int efx_siena_rss_cpus; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic unsigned int irq_adapt_low_thresh = 8000; 4062306a36Sopenharmony_cimodule_param(irq_adapt_low_thresh, uint, 0644); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(irq_adapt_low_thresh, 4262306a36Sopenharmony_ci "Threshold score for reducing IRQ moderation"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic unsigned int irq_adapt_high_thresh = 16000; 4562306a36Sopenharmony_cimodule_param(irq_adapt_high_thresh, uint, 0644); 4662306a36Sopenharmony_ciMODULE_PARM_DESC(irq_adapt_high_thresh, 4762306a36Sopenharmony_ci "Threshold score for increasing IRQ moderation"); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const struct efx_channel_type efx_default_channel_type; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/************* 5262306a36Sopenharmony_ci * INTERRUPTS 5362306a36Sopenharmony_ci *************/ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic unsigned int count_online_cores(struct efx_nic *efx, bool local_node) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci cpumask_var_t filter_mask; 5862306a36Sopenharmony_ci unsigned int count; 5962306a36Sopenharmony_ci int cpu; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (unlikely(!zalloc_cpumask_var(&filter_mask, GFP_KERNEL))) { 6262306a36Sopenharmony_ci netif_warn(efx, probe, efx->net_dev, 6362306a36Sopenharmony_ci "RSS disabled due to allocation failure\n"); 6462306a36Sopenharmony_ci return 1; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci cpumask_copy(filter_mask, cpu_online_mask); 6862306a36Sopenharmony_ci if (local_node) 6962306a36Sopenharmony_ci cpumask_and(filter_mask, filter_mask, 7062306a36Sopenharmony_ci cpumask_of_pcibus(efx->pci_dev->bus)); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci count = 0; 7362306a36Sopenharmony_ci for_each_cpu(cpu, filter_mask) { 7462306a36Sopenharmony_ci ++count; 7562306a36Sopenharmony_ci cpumask_andnot(filter_mask, filter_mask, topology_sibling_cpumask(cpu)); 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci free_cpumask_var(filter_mask); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return count; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic unsigned int efx_wanted_parallelism(struct efx_nic *efx) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci unsigned int count; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (efx_siena_rss_cpus) { 8862306a36Sopenharmony_ci count = efx_siena_rss_cpus; 8962306a36Sopenharmony_ci } else { 9062306a36Sopenharmony_ci count = count_online_cores(efx, true); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* If no online CPUs in local node, fallback to any online CPUs */ 9362306a36Sopenharmony_ci if (count == 0) 9462306a36Sopenharmony_ci count = count_online_cores(efx, false); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (count > EFX_MAX_RX_QUEUES) { 9862306a36Sopenharmony_ci netif_cond_dbg(efx, probe, efx->net_dev, !efx_siena_rss_cpus, 9962306a36Sopenharmony_ci warn, 10062306a36Sopenharmony_ci "Reducing number of rx queues from %u to %u.\n", 10162306a36Sopenharmony_ci count, EFX_MAX_RX_QUEUES); 10262306a36Sopenharmony_ci count = EFX_MAX_RX_QUEUES; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* If RSS is requested for the PF *and* VFs then we can't write RSS 10662306a36Sopenharmony_ci * table entries that are inaccessible to VFs 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_SRIOV 10962306a36Sopenharmony_ci if (efx->type->sriov_wanted) { 11062306a36Sopenharmony_ci if (efx->type->sriov_wanted(efx) && efx_vf_size(efx) > 1 && 11162306a36Sopenharmony_ci count > efx_vf_size(efx)) { 11262306a36Sopenharmony_ci netif_warn(efx, probe, efx->net_dev, 11362306a36Sopenharmony_ci "Reducing number of RSS channels from %u to %u for " 11462306a36Sopenharmony_ci "VF support. Increase vf-msix-limit to use more " 11562306a36Sopenharmony_ci "channels on the PF.\n", 11662306a36Sopenharmony_ci count, efx_vf_size(efx)); 11762306a36Sopenharmony_ci count = efx_vf_size(efx); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci#endif 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return count; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int efx_allocate_msix_channels(struct efx_nic *efx, 12662306a36Sopenharmony_ci unsigned int max_channels, 12762306a36Sopenharmony_ci unsigned int extra_channels, 12862306a36Sopenharmony_ci unsigned int parallelism) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci unsigned int n_channels = parallelism; 13162306a36Sopenharmony_ci int vec_count; 13262306a36Sopenharmony_ci int tx_per_ev; 13362306a36Sopenharmony_ci int n_xdp_tx; 13462306a36Sopenharmony_ci int n_xdp_ev; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (efx_siena_separate_tx_channels) 13762306a36Sopenharmony_ci n_channels *= 2; 13862306a36Sopenharmony_ci n_channels += extra_channels; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* To allow XDP transmit to happen from arbitrary NAPI contexts 14162306a36Sopenharmony_ci * we allocate a TX queue per CPU. We share event queues across 14262306a36Sopenharmony_ci * multiple tx queues, assuming tx and ev queues are both 14362306a36Sopenharmony_ci * maximum size. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci tx_per_ev = EFX_MAX_EVQ_SIZE / EFX_TXQ_MAX_ENT(efx); 14662306a36Sopenharmony_ci tx_per_ev = min(tx_per_ev, EFX_MAX_TXQ_PER_CHANNEL); 14762306a36Sopenharmony_ci n_xdp_tx = num_possible_cpus(); 14862306a36Sopenharmony_ci n_xdp_ev = DIV_ROUND_UP(n_xdp_tx, tx_per_ev); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci vec_count = pci_msix_vec_count(efx->pci_dev); 15162306a36Sopenharmony_ci if (vec_count < 0) 15262306a36Sopenharmony_ci return vec_count; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci max_channels = min_t(unsigned int, vec_count, max_channels); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Check resources. 15762306a36Sopenharmony_ci * We need a channel per event queue, plus a VI per tx queue. 15862306a36Sopenharmony_ci * This may be more pessimistic than it needs to be. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci if (n_channels >= max_channels) { 16162306a36Sopenharmony_ci efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED; 16262306a36Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 16362306a36Sopenharmony_ci "Insufficient resources for %d XDP event queues (%d other channels, max %d)\n", 16462306a36Sopenharmony_ci n_xdp_ev, n_channels, max_channels); 16562306a36Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 16662306a36Sopenharmony_ci "XDP_TX and XDP_REDIRECT might decrease device's performance\n"); 16762306a36Sopenharmony_ci } else if (n_channels + n_xdp_tx > efx->max_vis) { 16862306a36Sopenharmony_ci efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED; 16962306a36Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 17062306a36Sopenharmony_ci "Insufficient resources for %d XDP TX queues (%d other channels, max VIs %d)\n", 17162306a36Sopenharmony_ci n_xdp_tx, n_channels, efx->max_vis); 17262306a36Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 17362306a36Sopenharmony_ci "XDP_TX and XDP_REDIRECT might decrease device's performance\n"); 17462306a36Sopenharmony_ci } else if (n_channels + n_xdp_ev > max_channels) { 17562306a36Sopenharmony_ci efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_SHARED; 17662306a36Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 17762306a36Sopenharmony_ci "Insufficient resources for %d XDP event queues (%d other channels, max %d)\n", 17862306a36Sopenharmony_ci n_xdp_ev, n_channels, max_channels); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci n_xdp_ev = max_channels - n_channels; 18162306a36Sopenharmony_ci netif_warn(efx, drv, efx->net_dev, 18262306a36Sopenharmony_ci "XDP_TX and XDP_REDIRECT will work with reduced performance (%d cpus/tx_queue)\n", 18362306a36Sopenharmony_ci DIV_ROUND_UP(n_xdp_tx, tx_per_ev * n_xdp_ev)); 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_DEDICATED; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (efx->xdp_txq_queues_mode != EFX_XDP_TX_QUEUES_BORROWED) { 18962306a36Sopenharmony_ci efx->n_xdp_channels = n_xdp_ev; 19062306a36Sopenharmony_ci efx->xdp_tx_per_channel = tx_per_ev; 19162306a36Sopenharmony_ci efx->xdp_tx_queue_count = n_xdp_tx; 19262306a36Sopenharmony_ci n_channels += n_xdp_ev; 19362306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 19462306a36Sopenharmony_ci "Allocating %d TX and %d event queues for XDP\n", 19562306a36Sopenharmony_ci n_xdp_ev * tx_per_ev, n_xdp_ev); 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci efx->n_xdp_channels = 0; 19862306a36Sopenharmony_ci efx->xdp_tx_per_channel = 0; 19962306a36Sopenharmony_ci efx->xdp_tx_queue_count = n_xdp_tx; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (vec_count < n_channels) { 20362306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 20462306a36Sopenharmony_ci "WARNING: Insufficient MSI-X vectors available (%d < %u).\n", 20562306a36Sopenharmony_ci vec_count, n_channels); 20662306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 20762306a36Sopenharmony_ci "WARNING: Performance may be reduced.\n"); 20862306a36Sopenharmony_ci n_channels = vec_count; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci n_channels = min(n_channels, max_channels); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci efx->n_channels = n_channels; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Ignore XDP tx channels when creating rx channels. */ 21662306a36Sopenharmony_ci n_channels -= efx->n_xdp_channels; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (efx_siena_separate_tx_channels) { 21962306a36Sopenharmony_ci efx->n_tx_channels = 22062306a36Sopenharmony_ci min(max(n_channels / 2, 1U), 22162306a36Sopenharmony_ci efx->max_tx_channels); 22262306a36Sopenharmony_ci efx->tx_channel_offset = 22362306a36Sopenharmony_ci n_channels - efx->n_tx_channels; 22462306a36Sopenharmony_ci efx->n_rx_channels = 22562306a36Sopenharmony_ci max(n_channels - 22662306a36Sopenharmony_ci efx->n_tx_channels, 1U); 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci efx->n_tx_channels = min(n_channels, efx->max_tx_channels); 22962306a36Sopenharmony_ci efx->tx_channel_offset = 0; 23062306a36Sopenharmony_ci efx->n_rx_channels = n_channels; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci efx->n_rx_channels = min(efx->n_rx_channels, parallelism); 23462306a36Sopenharmony_ci efx->n_tx_channels = min(efx->n_tx_channels, parallelism); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci efx->xdp_channel_offset = n_channels; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 23962306a36Sopenharmony_ci "Allocating %u RX channels\n", 24062306a36Sopenharmony_ci efx->n_rx_channels); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return efx->n_channels; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* Probe the number and type of interrupts we are able to obtain, and 24662306a36Sopenharmony_ci * the resulting numbers of channels and RX queues. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ciint efx_siena_probe_interrupts(struct efx_nic *efx) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci unsigned int extra_channels = 0; 25162306a36Sopenharmony_ci unsigned int rss_spread; 25262306a36Sopenharmony_ci unsigned int i, j; 25362306a36Sopenharmony_ci int rc; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for (i = 0; i < EFX_MAX_EXTRA_CHANNELS; i++) 25662306a36Sopenharmony_ci if (efx->extra_channel_type[i]) 25762306a36Sopenharmony_ci ++extra_channels; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (efx->interrupt_mode == EFX_INT_MODE_MSIX) { 26062306a36Sopenharmony_ci unsigned int parallelism = efx_wanted_parallelism(efx); 26162306a36Sopenharmony_ci struct msix_entry xentries[EFX_MAX_CHANNELS]; 26262306a36Sopenharmony_ci unsigned int n_channels; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci rc = efx_allocate_msix_channels(efx, efx->max_channels, 26562306a36Sopenharmony_ci extra_channels, parallelism); 26662306a36Sopenharmony_ci if (rc >= 0) { 26762306a36Sopenharmony_ci n_channels = rc; 26862306a36Sopenharmony_ci for (i = 0; i < n_channels; i++) 26962306a36Sopenharmony_ci xentries[i].entry = i; 27062306a36Sopenharmony_ci rc = pci_enable_msix_range(efx->pci_dev, xentries, 1, 27162306a36Sopenharmony_ci n_channels); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci if (rc < 0) { 27462306a36Sopenharmony_ci /* Fall back to single channel MSI */ 27562306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 27662306a36Sopenharmony_ci "could not enable MSI-X\n"); 27762306a36Sopenharmony_ci if (efx->type->min_interrupt_mode >= EFX_INT_MODE_MSI) 27862306a36Sopenharmony_ci efx->interrupt_mode = EFX_INT_MODE_MSI; 27962306a36Sopenharmony_ci else 28062306a36Sopenharmony_ci return rc; 28162306a36Sopenharmony_ci } else if (rc < n_channels) { 28262306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 28362306a36Sopenharmony_ci "WARNING: Insufficient MSI-X vectors" 28462306a36Sopenharmony_ci " available (%d < %u).\n", rc, n_channels); 28562306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 28662306a36Sopenharmony_ci "WARNING: Performance may be reduced.\n"); 28762306a36Sopenharmony_ci n_channels = rc; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (rc > 0) { 29162306a36Sopenharmony_ci for (i = 0; i < efx->n_channels; i++) 29262306a36Sopenharmony_ci efx_get_channel(efx, i)->irq = 29362306a36Sopenharmony_ci xentries[i].vector; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Try single interrupt MSI */ 29862306a36Sopenharmony_ci if (efx->interrupt_mode == EFX_INT_MODE_MSI) { 29962306a36Sopenharmony_ci efx->n_channels = 1; 30062306a36Sopenharmony_ci efx->n_rx_channels = 1; 30162306a36Sopenharmony_ci efx->n_tx_channels = 1; 30262306a36Sopenharmony_ci efx->tx_channel_offset = 0; 30362306a36Sopenharmony_ci efx->n_xdp_channels = 0; 30462306a36Sopenharmony_ci efx->xdp_channel_offset = efx->n_channels; 30562306a36Sopenharmony_ci efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED; 30662306a36Sopenharmony_ci rc = pci_enable_msi(efx->pci_dev); 30762306a36Sopenharmony_ci if (rc == 0) { 30862306a36Sopenharmony_ci efx_get_channel(efx, 0)->irq = efx->pci_dev->irq; 30962306a36Sopenharmony_ci } else { 31062306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 31162306a36Sopenharmony_ci "could not enable MSI\n"); 31262306a36Sopenharmony_ci if (efx->type->min_interrupt_mode >= EFX_INT_MODE_LEGACY) 31362306a36Sopenharmony_ci efx->interrupt_mode = EFX_INT_MODE_LEGACY; 31462306a36Sopenharmony_ci else 31562306a36Sopenharmony_ci return rc; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Assume legacy interrupts */ 32062306a36Sopenharmony_ci if (efx->interrupt_mode == EFX_INT_MODE_LEGACY) { 32162306a36Sopenharmony_ci efx->n_channels = 1 + (efx_siena_separate_tx_channels ? 1 : 0); 32262306a36Sopenharmony_ci efx->n_rx_channels = 1; 32362306a36Sopenharmony_ci efx->n_tx_channels = 1; 32462306a36Sopenharmony_ci efx->tx_channel_offset = efx_siena_separate_tx_channels ? 1 : 0; 32562306a36Sopenharmony_ci efx->n_xdp_channels = 0; 32662306a36Sopenharmony_ci efx->xdp_channel_offset = efx->n_channels; 32762306a36Sopenharmony_ci efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED; 32862306a36Sopenharmony_ci efx->legacy_irq = efx->pci_dev->irq; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Assign extra channels if possible, before XDP channels */ 33262306a36Sopenharmony_ci efx->n_extra_tx_channels = 0; 33362306a36Sopenharmony_ci j = efx->xdp_channel_offset; 33462306a36Sopenharmony_ci for (i = 0; i < EFX_MAX_EXTRA_CHANNELS; i++) { 33562306a36Sopenharmony_ci if (!efx->extra_channel_type[i]) 33662306a36Sopenharmony_ci continue; 33762306a36Sopenharmony_ci if (j <= efx->tx_channel_offset + efx->n_tx_channels) { 33862306a36Sopenharmony_ci efx->extra_channel_type[i]->handle_no_channel(efx); 33962306a36Sopenharmony_ci } else { 34062306a36Sopenharmony_ci --j; 34162306a36Sopenharmony_ci efx_get_channel(efx, j)->type = 34262306a36Sopenharmony_ci efx->extra_channel_type[i]; 34362306a36Sopenharmony_ci if (efx_channel_has_tx_queues(efx_get_channel(efx, j))) 34462306a36Sopenharmony_ci efx->n_extra_tx_channels++; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci rss_spread = efx->n_rx_channels; 34962306a36Sopenharmony_ci /* RSS might be usable on VFs even if it is disabled on the PF */ 35062306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_SRIOV 35162306a36Sopenharmony_ci if (efx->type->sriov_wanted) { 35262306a36Sopenharmony_ci efx->rss_spread = ((rss_spread > 1 || 35362306a36Sopenharmony_ci !efx->type->sriov_wanted(efx)) ? 35462306a36Sopenharmony_ci rss_spread : efx_vf_size(efx)); 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci#endif 35862306a36Sopenharmony_ci efx->rss_spread = rss_spread; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci#if defined(CONFIG_SMP) 36462306a36Sopenharmony_civoid efx_siena_set_interrupt_affinity(struct efx_nic *efx) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci const struct cpumask *numa_mask = cpumask_of_pcibus(efx->pci_dev->bus); 36762306a36Sopenharmony_ci struct efx_channel *channel; 36862306a36Sopenharmony_ci unsigned int cpu; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* If no online CPUs in local node, fallback to any online CPU */ 37162306a36Sopenharmony_ci if (cpumask_first_and(cpu_online_mask, numa_mask) >= nr_cpu_ids) 37262306a36Sopenharmony_ci numa_mask = cpu_online_mask; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci cpu = -1; 37562306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 37662306a36Sopenharmony_ci cpu = cpumask_next_and(cpu, cpu_online_mask, numa_mask); 37762306a36Sopenharmony_ci if (cpu >= nr_cpu_ids) 37862306a36Sopenharmony_ci cpu = cpumask_first_and(cpu_online_mask, numa_mask); 37962306a36Sopenharmony_ci irq_set_affinity_hint(channel->irq, cpumask_of(cpu)); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_civoid efx_siena_clear_interrupt_affinity(struct efx_nic *efx) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct efx_channel *channel; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci efx_for_each_channel(channel, efx) 38862306a36Sopenharmony_ci irq_set_affinity_hint(channel->irq, NULL); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci#else 39162306a36Sopenharmony_civoid 39262306a36Sopenharmony_ciefx_siena_set_interrupt_affinity(struct efx_nic *efx __always_unused) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_civoid 39762306a36Sopenharmony_ciefx_siena_clear_interrupt_affinity(struct efx_nic *efx __always_unused) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_civoid efx_siena_remove_interrupts(struct efx_nic *efx) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct efx_channel *channel; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Remove MSI/MSI-X interrupts */ 40762306a36Sopenharmony_ci efx_for_each_channel(channel, efx) 40862306a36Sopenharmony_ci channel->irq = 0; 40962306a36Sopenharmony_ci pci_disable_msi(efx->pci_dev); 41062306a36Sopenharmony_ci pci_disable_msix(efx->pci_dev); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Remove legacy interrupt */ 41362306a36Sopenharmony_ci efx->legacy_irq = 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/*************** 41762306a36Sopenharmony_ci * EVENT QUEUES 41862306a36Sopenharmony_ci ***************/ 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* Create event queue 42162306a36Sopenharmony_ci * Event queue memory allocations are done only once. If the channel 42262306a36Sopenharmony_ci * is reset, the memory buffer will be reused; this guards against 42362306a36Sopenharmony_ci * errors during channel reset and also simplifies interrupt handling. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_cistatic int efx_probe_eventq(struct efx_channel *channel) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct efx_nic *efx = channel->efx; 42862306a36Sopenharmony_ci unsigned long entries; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci netif_dbg(efx, probe, efx->net_dev, 43162306a36Sopenharmony_ci "chan %d create event queue\n", channel->channel); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Build an event queue with room for one event per tx and rx buffer, 43462306a36Sopenharmony_ci * plus some extra for link state events and MCDI completions. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci entries = roundup_pow_of_two(efx->rxq_entries + efx->txq_entries + 128); 43762306a36Sopenharmony_ci EFX_WARN_ON_PARANOID(entries > EFX_MAX_EVQ_SIZE); 43862306a36Sopenharmony_ci channel->eventq_mask = max(entries, EFX_MIN_EVQ_SIZE) - 1; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return efx_nic_probe_eventq(channel); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/* Prepare channel's event queue */ 44462306a36Sopenharmony_cistatic int efx_init_eventq(struct efx_channel *channel) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct efx_nic *efx = channel->efx; 44762306a36Sopenharmony_ci int rc; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci EFX_WARN_ON_PARANOID(channel->eventq_init); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 45262306a36Sopenharmony_ci "chan %d init event queue\n", channel->channel); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci rc = efx_nic_init_eventq(channel); 45562306a36Sopenharmony_ci if (rc == 0) { 45662306a36Sopenharmony_ci efx->type->push_irq_moderation(channel); 45762306a36Sopenharmony_ci channel->eventq_read_ptr = 0; 45862306a36Sopenharmony_ci channel->eventq_init = true; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci return rc; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/* Enable event queue processing and NAPI */ 46462306a36Sopenharmony_civoid efx_siena_start_eventq(struct efx_channel *channel) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci netif_dbg(channel->efx, ifup, channel->efx->net_dev, 46762306a36Sopenharmony_ci "chan %d start event queue\n", channel->channel); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Make sure the NAPI handler sees the enabled flag set */ 47062306a36Sopenharmony_ci channel->enabled = true; 47162306a36Sopenharmony_ci smp_wmb(); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci napi_enable(&channel->napi_str); 47462306a36Sopenharmony_ci efx_nic_eventq_read_ack(channel); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci/* Disable event queue processing and NAPI */ 47862306a36Sopenharmony_civoid efx_siena_stop_eventq(struct efx_channel *channel) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci if (!channel->enabled) 48162306a36Sopenharmony_ci return; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci napi_disable(&channel->napi_str); 48462306a36Sopenharmony_ci channel->enabled = false; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic void efx_fini_eventq(struct efx_channel *channel) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci if (!channel->eventq_init) 49062306a36Sopenharmony_ci return; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci netif_dbg(channel->efx, drv, channel->efx->net_dev, 49362306a36Sopenharmony_ci "chan %d fini event queue\n", channel->channel); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci efx_nic_fini_eventq(channel); 49662306a36Sopenharmony_ci channel->eventq_init = false; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic void efx_remove_eventq(struct efx_channel *channel) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci netif_dbg(channel->efx, drv, channel->efx->net_dev, 50262306a36Sopenharmony_ci "chan %d remove event queue\n", channel->channel); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci efx_nic_remove_eventq(channel); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/************************************************************************** 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * Channel handling 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci *************************************************************************/ 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 51462306a36Sopenharmony_cistatic void efx_filter_rfs_expire(struct work_struct *data) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(data); 51762306a36Sopenharmony_ci struct efx_channel *channel; 51862306a36Sopenharmony_ci unsigned int time, quota; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci channel = container_of(dwork, struct efx_channel, filter_work); 52162306a36Sopenharmony_ci time = jiffies - channel->rfs_last_expiry; 52262306a36Sopenharmony_ci quota = channel->rfs_filter_count * time / (30 * HZ); 52362306a36Sopenharmony_ci if (quota >= 20 && __efx_siena_filter_rfs_expire(channel, 52462306a36Sopenharmony_ci min(channel->rfs_filter_count, quota))) 52562306a36Sopenharmony_ci channel->rfs_last_expiry += time; 52662306a36Sopenharmony_ci /* Ensure we do more work eventually even if NAPI poll is not happening */ 52762306a36Sopenharmony_ci schedule_delayed_work(dwork, 30 * HZ); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci#endif 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/* Allocate and initialise a channel structure. */ 53262306a36Sopenharmony_cistatic struct efx_channel *efx_alloc_channel(struct efx_nic *efx, int i) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct efx_rx_queue *rx_queue; 53562306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 53662306a36Sopenharmony_ci struct efx_channel *channel; 53762306a36Sopenharmony_ci int j; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci channel = kzalloc(sizeof(*channel), GFP_KERNEL); 54062306a36Sopenharmony_ci if (!channel) 54162306a36Sopenharmony_ci return NULL; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci channel->efx = efx; 54462306a36Sopenharmony_ci channel->channel = i; 54562306a36Sopenharmony_ci channel->type = &efx_default_channel_type; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci for (j = 0; j < EFX_MAX_TXQ_PER_CHANNEL; j++) { 54862306a36Sopenharmony_ci tx_queue = &channel->tx_queue[j]; 54962306a36Sopenharmony_ci tx_queue->efx = efx; 55062306a36Sopenharmony_ci tx_queue->queue = -1; 55162306a36Sopenharmony_ci tx_queue->label = j; 55262306a36Sopenharmony_ci tx_queue->channel = channel; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 55662306a36Sopenharmony_ci INIT_DELAYED_WORK(&channel->filter_work, efx_filter_rfs_expire); 55762306a36Sopenharmony_ci#endif 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci rx_queue = &channel->rx_queue; 56062306a36Sopenharmony_ci rx_queue->efx = efx; 56162306a36Sopenharmony_ci timer_setup(&rx_queue->slow_fill, efx_siena_rx_slow_fill, 0); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci return channel; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ciint efx_siena_init_channels(struct efx_nic *efx) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci unsigned int i; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci for (i = 0; i < EFX_MAX_CHANNELS; i++) { 57162306a36Sopenharmony_ci efx->channel[i] = efx_alloc_channel(efx, i); 57262306a36Sopenharmony_ci if (!efx->channel[i]) 57362306a36Sopenharmony_ci return -ENOMEM; 57462306a36Sopenharmony_ci efx->msi_context[i].efx = efx; 57562306a36Sopenharmony_ci efx->msi_context[i].index = i; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* Higher numbered interrupt modes are less capable! */ 57962306a36Sopenharmony_ci efx->interrupt_mode = min(efx->type->min_interrupt_mode, 58062306a36Sopenharmony_ci efx_siena_interrupt_mode); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci efx->max_channels = EFX_MAX_CHANNELS; 58362306a36Sopenharmony_ci efx->max_tx_channels = EFX_MAX_CHANNELS; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_civoid efx_siena_fini_channels(struct efx_nic *efx) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci unsigned int i; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci for (i = 0; i < EFX_MAX_CHANNELS; i++) 59362306a36Sopenharmony_ci if (efx->channel[i]) { 59462306a36Sopenharmony_ci kfree(efx->channel[i]); 59562306a36Sopenharmony_ci efx->channel[i] = NULL; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* Allocate and initialise a channel structure, copying parameters 60062306a36Sopenharmony_ci * (but not resources) from an old channel structure. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_cistatic 60362306a36Sopenharmony_cistruct efx_channel *efx_copy_channel(const struct efx_channel *old_channel) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct efx_rx_queue *rx_queue; 60662306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 60762306a36Sopenharmony_ci struct efx_channel *channel; 60862306a36Sopenharmony_ci int j; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci channel = kmalloc(sizeof(*channel), GFP_KERNEL); 61162306a36Sopenharmony_ci if (!channel) 61262306a36Sopenharmony_ci return NULL; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci *channel = *old_channel; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci channel->napi_dev = NULL; 61762306a36Sopenharmony_ci INIT_HLIST_NODE(&channel->napi_str.napi_hash_node); 61862306a36Sopenharmony_ci channel->napi_str.napi_id = 0; 61962306a36Sopenharmony_ci channel->napi_str.state = 0; 62062306a36Sopenharmony_ci memset(&channel->eventq, 0, sizeof(channel->eventq)); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci for (j = 0; j < EFX_MAX_TXQ_PER_CHANNEL; j++) { 62362306a36Sopenharmony_ci tx_queue = &channel->tx_queue[j]; 62462306a36Sopenharmony_ci if (tx_queue->channel) 62562306a36Sopenharmony_ci tx_queue->channel = channel; 62662306a36Sopenharmony_ci tx_queue->buffer = NULL; 62762306a36Sopenharmony_ci tx_queue->cb_page = NULL; 62862306a36Sopenharmony_ci memset(&tx_queue->txd, 0, sizeof(tx_queue->txd)); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci rx_queue = &channel->rx_queue; 63262306a36Sopenharmony_ci rx_queue->buffer = NULL; 63362306a36Sopenharmony_ci memset(&rx_queue->rxd, 0, sizeof(rx_queue->rxd)); 63462306a36Sopenharmony_ci timer_setup(&rx_queue->slow_fill, efx_siena_rx_slow_fill, 0); 63562306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 63662306a36Sopenharmony_ci INIT_DELAYED_WORK(&channel->filter_work, efx_filter_rfs_expire); 63762306a36Sopenharmony_ci#endif 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return channel; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int efx_probe_channel(struct efx_channel *channel) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 64562306a36Sopenharmony_ci struct efx_rx_queue *rx_queue; 64662306a36Sopenharmony_ci int rc; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci netif_dbg(channel->efx, probe, channel->efx->net_dev, 64962306a36Sopenharmony_ci "creating channel %d\n", channel->channel); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci rc = channel->type->pre_probe(channel); 65262306a36Sopenharmony_ci if (rc) 65362306a36Sopenharmony_ci goto fail; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci rc = efx_probe_eventq(channel); 65662306a36Sopenharmony_ci if (rc) 65762306a36Sopenharmony_ci goto fail; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) { 66062306a36Sopenharmony_ci rc = efx_siena_probe_tx_queue(tx_queue); 66162306a36Sopenharmony_ci if (rc) 66262306a36Sopenharmony_ci goto fail; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci efx_for_each_channel_rx_queue(rx_queue, channel) { 66662306a36Sopenharmony_ci rc = efx_siena_probe_rx_queue(rx_queue); 66762306a36Sopenharmony_ci if (rc) 66862306a36Sopenharmony_ci goto fail; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci channel->rx_list = NULL; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci return 0; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cifail: 67662306a36Sopenharmony_ci efx_siena_remove_channel(channel); 67762306a36Sopenharmony_ci return rc; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic void efx_get_channel_name(struct efx_channel *channel, char *buf, 68162306a36Sopenharmony_ci size_t len) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct efx_nic *efx = channel->efx; 68462306a36Sopenharmony_ci const char *type; 68562306a36Sopenharmony_ci int number; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci number = channel->channel; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (number >= efx->xdp_channel_offset && 69062306a36Sopenharmony_ci !WARN_ON_ONCE(!efx->n_xdp_channels)) { 69162306a36Sopenharmony_ci type = "-xdp"; 69262306a36Sopenharmony_ci number -= efx->xdp_channel_offset; 69362306a36Sopenharmony_ci } else if (efx->tx_channel_offset == 0) { 69462306a36Sopenharmony_ci type = ""; 69562306a36Sopenharmony_ci } else if (number < efx->tx_channel_offset) { 69662306a36Sopenharmony_ci type = "-rx"; 69762306a36Sopenharmony_ci } else { 69862306a36Sopenharmony_ci type = "-tx"; 69962306a36Sopenharmony_ci number -= efx->tx_channel_offset; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci snprintf(buf, len, "%s%s-%d", efx->name, type, number); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_civoid efx_siena_set_channel_names(struct efx_nic *efx) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct efx_channel *channel; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci efx_for_each_channel(channel, efx) 70962306a36Sopenharmony_ci channel->type->get_name(channel, 71062306a36Sopenharmony_ci efx->msi_context[channel->channel].name, 71162306a36Sopenharmony_ci sizeof(efx->msi_context[0].name)); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ciint efx_siena_probe_channels(struct efx_nic *efx) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct efx_channel *channel; 71762306a36Sopenharmony_ci int rc; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* Restart special buffer allocation */ 72062306a36Sopenharmony_ci efx->next_buffer_table = 0; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Probe channels in reverse, so that any 'extra' channels 72362306a36Sopenharmony_ci * use the start of the buffer table. This allows the traffic 72462306a36Sopenharmony_ci * channels to be resized without moving them or wasting the 72562306a36Sopenharmony_ci * entries before them. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci efx_for_each_channel_rev(channel, efx) { 72862306a36Sopenharmony_ci rc = efx_probe_channel(channel); 72962306a36Sopenharmony_ci if (rc) { 73062306a36Sopenharmony_ci netif_err(efx, probe, efx->net_dev, 73162306a36Sopenharmony_ci "failed to create channel %d\n", 73262306a36Sopenharmony_ci channel->channel); 73362306a36Sopenharmony_ci goto fail; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci efx_siena_set_channel_names(efx); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cifail: 74162306a36Sopenharmony_ci efx_siena_remove_channels(efx); 74262306a36Sopenharmony_ci return rc; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_civoid efx_siena_remove_channel(struct efx_channel *channel) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 74862306a36Sopenharmony_ci struct efx_rx_queue *rx_queue; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci netif_dbg(channel->efx, drv, channel->efx->net_dev, 75162306a36Sopenharmony_ci "destroy chan %d\n", channel->channel); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci efx_for_each_channel_rx_queue(rx_queue, channel) 75462306a36Sopenharmony_ci efx_siena_remove_rx_queue(rx_queue); 75562306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) 75662306a36Sopenharmony_ci efx_siena_remove_tx_queue(tx_queue); 75762306a36Sopenharmony_ci efx_remove_eventq(channel); 75862306a36Sopenharmony_ci channel->type->post_remove(channel); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_civoid efx_siena_remove_channels(struct efx_nic *efx) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct efx_channel *channel; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci efx_for_each_channel(channel, efx) 76662306a36Sopenharmony_ci efx_siena_remove_channel(channel); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci kfree(efx->xdp_tx_queues); 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int efx_set_xdp_tx_queue(struct efx_nic *efx, int xdp_queue_number, 77262306a36Sopenharmony_ci struct efx_tx_queue *tx_queue) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci if (xdp_queue_number >= efx->xdp_tx_queue_count) 77562306a36Sopenharmony_ci return -EINVAL; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 77862306a36Sopenharmony_ci "Channel %u TXQ %u is XDP %u, HW %u\n", 77962306a36Sopenharmony_ci tx_queue->channel->channel, tx_queue->label, 78062306a36Sopenharmony_ci xdp_queue_number, tx_queue->queue); 78162306a36Sopenharmony_ci efx->xdp_tx_queues[xdp_queue_number] = tx_queue; 78262306a36Sopenharmony_ci return 0; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic void efx_set_xdp_channels(struct efx_nic *efx) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 78862306a36Sopenharmony_ci struct efx_channel *channel; 78962306a36Sopenharmony_ci unsigned int next_queue = 0; 79062306a36Sopenharmony_ci int xdp_queue_number = 0; 79162306a36Sopenharmony_ci int rc; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* We need to mark which channels really have RX and TX 79462306a36Sopenharmony_ci * queues, and adjust the TX queue numbers if we have separate 79562306a36Sopenharmony_ci * RX-only and TX-only channels. 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 79862306a36Sopenharmony_ci if (channel->channel < efx->tx_channel_offset) 79962306a36Sopenharmony_ci continue; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (efx_channel_is_xdp_tx(channel)) { 80262306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) { 80362306a36Sopenharmony_ci tx_queue->queue = next_queue++; 80462306a36Sopenharmony_ci rc = efx_set_xdp_tx_queue(efx, xdp_queue_number, 80562306a36Sopenharmony_ci tx_queue); 80662306a36Sopenharmony_ci if (rc == 0) 80762306a36Sopenharmony_ci xdp_queue_number++; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci } else { 81062306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) { 81162306a36Sopenharmony_ci tx_queue->queue = next_queue++; 81262306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 81362306a36Sopenharmony_ci "Channel %u TXQ %u is HW %u\n", 81462306a36Sopenharmony_ci channel->channel, tx_queue->label, 81562306a36Sopenharmony_ci tx_queue->queue); 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* If XDP is borrowing queues from net stack, it must 81962306a36Sopenharmony_ci * use the queue with no csum offload, which is the 82062306a36Sopenharmony_ci * first one of the channel 82162306a36Sopenharmony_ci * (note: tx_queue_by_type is not initialized yet) 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ci if (efx->xdp_txq_queues_mode == 82462306a36Sopenharmony_ci EFX_XDP_TX_QUEUES_BORROWED) { 82562306a36Sopenharmony_ci tx_queue = &channel->tx_queue[0]; 82662306a36Sopenharmony_ci rc = efx_set_xdp_tx_queue(efx, xdp_queue_number, 82762306a36Sopenharmony_ci tx_queue); 82862306a36Sopenharmony_ci if (rc == 0) 82962306a36Sopenharmony_ci xdp_queue_number++; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci WARN_ON(efx->xdp_txq_queues_mode == EFX_XDP_TX_QUEUES_DEDICATED && 83462306a36Sopenharmony_ci xdp_queue_number != efx->xdp_tx_queue_count); 83562306a36Sopenharmony_ci WARN_ON(efx->xdp_txq_queues_mode != EFX_XDP_TX_QUEUES_DEDICATED && 83662306a36Sopenharmony_ci xdp_queue_number > efx->xdp_tx_queue_count); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* If we have more CPUs than assigned XDP TX queues, assign the already 83962306a36Sopenharmony_ci * existing queues to the exceeding CPUs 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci next_queue = 0; 84262306a36Sopenharmony_ci while (xdp_queue_number < efx->xdp_tx_queue_count) { 84362306a36Sopenharmony_ci tx_queue = efx->xdp_tx_queues[next_queue++]; 84462306a36Sopenharmony_ci rc = efx_set_xdp_tx_queue(efx, xdp_queue_number, tx_queue); 84562306a36Sopenharmony_ci if (rc == 0) 84662306a36Sopenharmony_ci xdp_queue_number++; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic int efx_soft_enable_interrupts(struct efx_nic *efx); 85162306a36Sopenharmony_cistatic void efx_soft_disable_interrupts(struct efx_nic *efx); 85262306a36Sopenharmony_cistatic void efx_init_napi_channel(struct efx_channel *channel); 85362306a36Sopenharmony_cistatic void efx_fini_napi_channel(struct efx_channel *channel); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ciint efx_siena_realloc_channels(struct efx_nic *efx, u32 rxq_entries, 85662306a36Sopenharmony_ci u32 txq_entries) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct efx_channel *other_channel[EFX_MAX_CHANNELS], *channel; 85962306a36Sopenharmony_ci unsigned int i, next_buffer_table = 0; 86062306a36Sopenharmony_ci u32 old_rxq_entries, old_txq_entries; 86162306a36Sopenharmony_ci int rc, rc2; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci rc = efx_check_disabled(efx); 86462306a36Sopenharmony_ci if (rc) 86562306a36Sopenharmony_ci return rc; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci /* Not all channels should be reallocated. We must avoid 86862306a36Sopenharmony_ci * reallocating their buffer table entries. 86962306a36Sopenharmony_ci */ 87062306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 87162306a36Sopenharmony_ci struct efx_rx_queue *rx_queue; 87262306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (channel->type->copy) 87562306a36Sopenharmony_ci continue; 87662306a36Sopenharmony_ci next_buffer_table = max(next_buffer_table, 87762306a36Sopenharmony_ci channel->eventq.index + 87862306a36Sopenharmony_ci channel->eventq.entries); 87962306a36Sopenharmony_ci efx_for_each_channel_rx_queue(rx_queue, channel) 88062306a36Sopenharmony_ci next_buffer_table = max(next_buffer_table, 88162306a36Sopenharmony_ci rx_queue->rxd.index + 88262306a36Sopenharmony_ci rx_queue->rxd.entries); 88362306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) 88462306a36Sopenharmony_ci next_buffer_table = max(next_buffer_table, 88562306a36Sopenharmony_ci tx_queue->txd.index + 88662306a36Sopenharmony_ci tx_queue->txd.entries); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci efx_device_detach_sync(efx); 89062306a36Sopenharmony_ci efx_siena_stop_all(efx); 89162306a36Sopenharmony_ci efx_soft_disable_interrupts(efx); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* Clone channels (where possible) */ 89462306a36Sopenharmony_ci memset(other_channel, 0, sizeof(other_channel)); 89562306a36Sopenharmony_ci for (i = 0; i < efx->n_channels; i++) { 89662306a36Sopenharmony_ci channel = efx->channel[i]; 89762306a36Sopenharmony_ci if (channel->type->copy) 89862306a36Sopenharmony_ci channel = channel->type->copy(channel); 89962306a36Sopenharmony_ci if (!channel) { 90062306a36Sopenharmony_ci rc = -ENOMEM; 90162306a36Sopenharmony_ci goto out; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci other_channel[i] = channel; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Swap entry counts and channel pointers */ 90762306a36Sopenharmony_ci old_rxq_entries = efx->rxq_entries; 90862306a36Sopenharmony_ci old_txq_entries = efx->txq_entries; 90962306a36Sopenharmony_ci efx->rxq_entries = rxq_entries; 91062306a36Sopenharmony_ci efx->txq_entries = txq_entries; 91162306a36Sopenharmony_ci for (i = 0; i < efx->n_channels; i++) 91262306a36Sopenharmony_ci swap(efx->channel[i], other_channel[i]); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* Restart buffer table allocation */ 91562306a36Sopenharmony_ci efx->next_buffer_table = next_buffer_table; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci for (i = 0; i < efx->n_channels; i++) { 91862306a36Sopenharmony_ci channel = efx->channel[i]; 91962306a36Sopenharmony_ci if (!channel->type->copy) 92062306a36Sopenharmony_ci continue; 92162306a36Sopenharmony_ci rc = efx_probe_channel(channel); 92262306a36Sopenharmony_ci if (rc) 92362306a36Sopenharmony_ci goto rollback; 92462306a36Sopenharmony_ci efx_init_napi_channel(efx->channel[i]); 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci efx_set_xdp_channels(efx); 92862306a36Sopenharmony_ciout: 92962306a36Sopenharmony_ci /* Destroy unused channel structures */ 93062306a36Sopenharmony_ci for (i = 0; i < efx->n_channels; i++) { 93162306a36Sopenharmony_ci channel = other_channel[i]; 93262306a36Sopenharmony_ci if (channel && channel->type->copy) { 93362306a36Sopenharmony_ci efx_fini_napi_channel(channel); 93462306a36Sopenharmony_ci efx_siena_remove_channel(channel); 93562306a36Sopenharmony_ci kfree(channel); 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci rc2 = efx_soft_enable_interrupts(efx); 94062306a36Sopenharmony_ci if (rc2) { 94162306a36Sopenharmony_ci rc = rc ? rc : rc2; 94262306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 94362306a36Sopenharmony_ci "unable to restart interrupts on channel reallocation\n"); 94462306a36Sopenharmony_ci efx_siena_schedule_reset(efx, RESET_TYPE_DISABLE); 94562306a36Sopenharmony_ci } else { 94662306a36Sopenharmony_ci efx_siena_start_all(efx); 94762306a36Sopenharmony_ci efx_device_attach_if_not_resetting(efx); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci return rc; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cirollback: 95262306a36Sopenharmony_ci /* Swap back */ 95362306a36Sopenharmony_ci efx->rxq_entries = old_rxq_entries; 95462306a36Sopenharmony_ci efx->txq_entries = old_txq_entries; 95562306a36Sopenharmony_ci for (i = 0; i < efx->n_channels; i++) 95662306a36Sopenharmony_ci swap(efx->channel[i], other_channel[i]); 95762306a36Sopenharmony_ci goto out; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ciint efx_siena_set_channels(struct efx_nic *efx) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct efx_channel *channel; 96362306a36Sopenharmony_ci int rc; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (efx->xdp_tx_queue_count) { 96662306a36Sopenharmony_ci EFX_WARN_ON_PARANOID(efx->xdp_tx_queues); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* Allocate array for XDP TX queue lookup. */ 96962306a36Sopenharmony_ci efx->xdp_tx_queues = kcalloc(efx->xdp_tx_queue_count, 97062306a36Sopenharmony_ci sizeof(*efx->xdp_tx_queues), 97162306a36Sopenharmony_ci GFP_KERNEL); 97262306a36Sopenharmony_ci if (!efx->xdp_tx_queues) 97362306a36Sopenharmony_ci return -ENOMEM; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 97762306a36Sopenharmony_ci if (channel->channel < efx->n_rx_channels) 97862306a36Sopenharmony_ci channel->rx_queue.core_index = channel->channel; 97962306a36Sopenharmony_ci else 98062306a36Sopenharmony_ci channel->rx_queue.core_index = -1; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci efx_set_xdp_channels(efx); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci rc = netif_set_real_num_tx_queues(efx->net_dev, efx->n_tx_channels); 98662306a36Sopenharmony_ci if (rc) 98762306a36Sopenharmony_ci return rc; 98862306a36Sopenharmony_ci return netif_set_real_num_rx_queues(efx->net_dev, efx->n_rx_channels); 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic bool efx_default_channel_want_txqs(struct efx_channel *channel) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci return channel->channel - channel->efx->tx_channel_offset < 99462306a36Sopenharmony_ci channel->efx->n_tx_channels; 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci/************* 99862306a36Sopenharmony_ci * START/STOP 99962306a36Sopenharmony_ci *************/ 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic int efx_soft_enable_interrupts(struct efx_nic *efx) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct efx_channel *channel, *end_channel; 100462306a36Sopenharmony_ci int rc; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci BUG_ON(efx->state == STATE_DISABLED); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci efx->irq_soft_enabled = true; 100962306a36Sopenharmony_ci smp_wmb(); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 101262306a36Sopenharmony_ci if (!channel->type->keep_eventq) { 101362306a36Sopenharmony_ci rc = efx_init_eventq(channel); 101462306a36Sopenharmony_ci if (rc) 101562306a36Sopenharmony_ci goto fail; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci efx_siena_start_eventq(channel); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci efx_siena_mcdi_mode_event(efx); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci return 0; 102362306a36Sopenharmony_cifail: 102462306a36Sopenharmony_ci end_channel = channel; 102562306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 102662306a36Sopenharmony_ci if (channel == end_channel) 102762306a36Sopenharmony_ci break; 102862306a36Sopenharmony_ci efx_siena_stop_eventq(channel); 102962306a36Sopenharmony_ci if (!channel->type->keep_eventq) 103062306a36Sopenharmony_ci efx_fini_eventq(channel); 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci return rc; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic void efx_soft_disable_interrupts(struct efx_nic *efx) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci struct efx_channel *channel; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (efx->state == STATE_DISABLED) 104162306a36Sopenharmony_ci return; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci efx_siena_mcdi_mode_poll(efx); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci efx->irq_soft_enabled = false; 104662306a36Sopenharmony_ci smp_wmb(); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (efx->legacy_irq) 104962306a36Sopenharmony_ci synchronize_irq(efx->legacy_irq); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 105262306a36Sopenharmony_ci if (channel->irq) 105362306a36Sopenharmony_ci synchronize_irq(channel->irq); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci efx_siena_stop_eventq(channel); 105662306a36Sopenharmony_ci if (!channel->type->keep_eventq) 105762306a36Sopenharmony_ci efx_fini_eventq(channel); 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* Flush the asynchronous MCDI request queue */ 106162306a36Sopenharmony_ci efx_siena_mcdi_flush_async(efx); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ciint efx_siena_enable_interrupts(struct efx_nic *efx) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct efx_channel *channel, *end_channel; 106762306a36Sopenharmony_ci int rc; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* TODO: Is this really a bug? */ 107062306a36Sopenharmony_ci BUG_ON(efx->state == STATE_DISABLED); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (efx->eeh_disabled_legacy_irq) { 107362306a36Sopenharmony_ci enable_irq(efx->legacy_irq); 107462306a36Sopenharmony_ci efx->eeh_disabled_legacy_irq = false; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci efx->type->irq_enable_master(efx); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 108062306a36Sopenharmony_ci if (channel->type->keep_eventq) { 108162306a36Sopenharmony_ci rc = efx_init_eventq(channel); 108262306a36Sopenharmony_ci if (rc) 108362306a36Sopenharmony_ci goto fail; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci rc = efx_soft_enable_interrupts(efx); 108862306a36Sopenharmony_ci if (rc) 108962306a36Sopenharmony_ci goto fail; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return 0; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cifail: 109462306a36Sopenharmony_ci end_channel = channel; 109562306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 109662306a36Sopenharmony_ci if (channel == end_channel) 109762306a36Sopenharmony_ci break; 109862306a36Sopenharmony_ci if (channel->type->keep_eventq) 109962306a36Sopenharmony_ci efx_fini_eventq(channel); 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci efx->type->irq_disable_non_ev(efx); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci return rc; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_civoid efx_siena_disable_interrupts(struct efx_nic *efx) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct efx_channel *channel; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci efx_soft_disable_interrupts(efx); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 111462306a36Sopenharmony_ci if (channel->type->keep_eventq) 111562306a36Sopenharmony_ci efx_fini_eventq(channel); 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci efx->type->irq_disable_non_ev(efx); 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_civoid efx_siena_start_channels(struct efx_nic *efx) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 112462306a36Sopenharmony_ci struct efx_rx_queue *rx_queue; 112562306a36Sopenharmony_ci struct efx_channel *channel; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci efx_for_each_channel_rev(channel, efx) { 112862306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) { 112962306a36Sopenharmony_ci efx_siena_init_tx_queue(tx_queue); 113062306a36Sopenharmony_ci atomic_inc(&efx->active_queues); 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci efx_for_each_channel_rx_queue(rx_queue, channel) { 113462306a36Sopenharmony_ci efx_siena_init_rx_queue(rx_queue); 113562306a36Sopenharmony_ci atomic_inc(&efx->active_queues); 113662306a36Sopenharmony_ci efx_siena_stop_eventq(channel); 113762306a36Sopenharmony_ci efx_siena_fast_push_rx_descriptors(rx_queue, false); 113862306a36Sopenharmony_ci efx_siena_start_eventq(channel); 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci WARN_ON(channel->rx_pkt_n_frags); 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_civoid efx_siena_stop_channels(struct efx_nic *efx) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 114862306a36Sopenharmony_ci struct efx_rx_queue *rx_queue; 114962306a36Sopenharmony_ci struct efx_channel *channel; 115062306a36Sopenharmony_ci int rc = 0; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* Stop RX refill */ 115362306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 115462306a36Sopenharmony_ci efx_for_each_channel_rx_queue(rx_queue, channel) 115562306a36Sopenharmony_ci rx_queue->refill_enabled = false; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 115962306a36Sopenharmony_ci /* RX packet processing is pipelined, so wait for the 116062306a36Sopenharmony_ci * NAPI handler to complete. At least event queue 0 116162306a36Sopenharmony_ci * might be kept active by non-data events, so don't 116262306a36Sopenharmony_ci * use napi_synchronize() but actually disable NAPI 116362306a36Sopenharmony_ci * temporarily. 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_ci if (efx_channel_has_rx_queue(channel)) { 116662306a36Sopenharmony_ci efx_siena_stop_eventq(channel); 116762306a36Sopenharmony_ci efx_siena_start_eventq(channel); 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (efx->type->fini_dmaq) 117262306a36Sopenharmony_ci rc = efx->type->fini_dmaq(efx); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (rc) { 117562306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, "failed to flush queues\n"); 117662306a36Sopenharmony_ci } else { 117762306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 117862306a36Sopenharmony_ci "successfully flushed all queues\n"); 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci efx_for_each_channel(channel, efx) { 118262306a36Sopenharmony_ci efx_for_each_channel_rx_queue(rx_queue, channel) 118362306a36Sopenharmony_ci efx_siena_fini_rx_queue(rx_queue); 118462306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) 118562306a36Sopenharmony_ci efx_siena_fini_tx_queue(tx_queue); 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci/************************************************************************** 119062306a36Sopenharmony_ci * 119162306a36Sopenharmony_ci * NAPI interface 119262306a36Sopenharmony_ci * 119362306a36Sopenharmony_ci *************************************************************************/ 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci/* Process channel's event queue 119662306a36Sopenharmony_ci * 119762306a36Sopenharmony_ci * This function is responsible for processing the event queue of a 119862306a36Sopenharmony_ci * single channel. The caller must guarantee that this function will 119962306a36Sopenharmony_ci * never be concurrently called more than once on the same channel, 120062306a36Sopenharmony_ci * though different channels may be being processed concurrently. 120162306a36Sopenharmony_ci */ 120262306a36Sopenharmony_cistatic int efx_process_channel(struct efx_channel *channel, int budget) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct efx_tx_queue *tx_queue; 120562306a36Sopenharmony_ci struct list_head rx_list; 120662306a36Sopenharmony_ci int spent; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (unlikely(!channel->enabled)) 120962306a36Sopenharmony_ci return 0; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* Prepare the batch receive list */ 121262306a36Sopenharmony_ci EFX_WARN_ON_PARANOID(channel->rx_list != NULL); 121362306a36Sopenharmony_ci INIT_LIST_HEAD(&rx_list); 121462306a36Sopenharmony_ci channel->rx_list = &rx_list; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) { 121762306a36Sopenharmony_ci tx_queue->pkts_compl = 0; 121862306a36Sopenharmony_ci tx_queue->bytes_compl = 0; 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci spent = efx_nic_process_eventq(channel, budget); 122262306a36Sopenharmony_ci if (spent && efx_channel_has_rx_queue(channel)) { 122362306a36Sopenharmony_ci struct efx_rx_queue *rx_queue = 122462306a36Sopenharmony_ci efx_channel_get_rx_queue(channel); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci efx_rx_flush_packet(channel); 122762306a36Sopenharmony_ci efx_siena_fast_push_rx_descriptors(rx_queue, true); 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* Update BQL */ 123162306a36Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) { 123262306a36Sopenharmony_ci if (tx_queue->bytes_compl) { 123362306a36Sopenharmony_ci netdev_tx_completed_queue(tx_queue->core_txq, 123462306a36Sopenharmony_ci tx_queue->pkts_compl, 123562306a36Sopenharmony_ci tx_queue->bytes_compl); 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci /* Receive any packets we queued up */ 124062306a36Sopenharmony_ci netif_receive_skb_list(channel->rx_list); 124162306a36Sopenharmony_ci channel->rx_list = NULL; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return spent; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic void efx_update_irq_mod(struct efx_nic *efx, struct efx_channel *channel) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci int step = efx->irq_mod_step_us; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (channel->irq_mod_score < irq_adapt_low_thresh) { 125162306a36Sopenharmony_ci if (channel->irq_moderation_us > step) { 125262306a36Sopenharmony_ci channel->irq_moderation_us -= step; 125362306a36Sopenharmony_ci efx->type->push_irq_moderation(channel); 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci } else if (channel->irq_mod_score > irq_adapt_high_thresh) { 125662306a36Sopenharmony_ci if (channel->irq_moderation_us < 125762306a36Sopenharmony_ci efx->irq_rx_moderation_us) { 125862306a36Sopenharmony_ci channel->irq_moderation_us += step; 125962306a36Sopenharmony_ci efx->type->push_irq_moderation(channel); 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci channel->irq_count = 0; 126462306a36Sopenharmony_ci channel->irq_mod_score = 0; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci/* NAPI poll handler 126862306a36Sopenharmony_ci * 126962306a36Sopenharmony_ci * NAPI guarantees serialisation of polls of the same device, which 127062306a36Sopenharmony_ci * provides the guarantee required by efx_process_channel(). 127162306a36Sopenharmony_ci */ 127262306a36Sopenharmony_cistatic int efx_poll(struct napi_struct *napi, int budget) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci struct efx_channel *channel = 127562306a36Sopenharmony_ci container_of(napi, struct efx_channel, napi_str); 127662306a36Sopenharmony_ci struct efx_nic *efx = channel->efx; 127762306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 127862306a36Sopenharmony_ci unsigned int time; 127962306a36Sopenharmony_ci#endif 128062306a36Sopenharmony_ci int spent; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci netif_vdbg(efx, intr, efx->net_dev, 128362306a36Sopenharmony_ci "channel %d NAPI poll executing on CPU %d\n", 128462306a36Sopenharmony_ci channel->channel, raw_smp_processor_id()); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci spent = efx_process_channel(channel, budget); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci xdp_do_flush_map(); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci if (spent < budget) { 129162306a36Sopenharmony_ci if (efx_channel_has_rx_queue(channel) && 129262306a36Sopenharmony_ci efx->irq_rx_adaptive && 129362306a36Sopenharmony_ci unlikely(++channel->irq_count == 1000)) { 129462306a36Sopenharmony_ci efx_update_irq_mod(efx, channel); 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci#ifdef CONFIG_RFS_ACCEL 129862306a36Sopenharmony_ci /* Perhaps expire some ARFS filters */ 129962306a36Sopenharmony_ci time = jiffies - channel->rfs_last_expiry; 130062306a36Sopenharmony_ci /* Would our quota be >= 20? */ 130162306a36Sopenharmony_ci if (channel->rfs_filter_count * time >= 600 * HZ) 130262306a36Sopenharmony_ci mod_delayed_work(system_wq, &channel->filter_work, 0); 130362306a36Sopenharmony_ci#endif 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* There is no race here; although napi_disable() will 130662306a36Sopenharmony_ci * only wait for napi_complete(), this isn't a problem 130762306a36Sopenharmony_ci * since efx_nic_eventq_read_ack() will have no effect if 130862306a36Sopenharmony_ci * interrupts have already been disabled. 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_ci if (napi_complete_done(napi, spent)) 131162306a36Sopenharmony_ci efx_nic_eventq_read_ack(channel); 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci return spent; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic void efx_init_napi_channel(struct efx_channel *channel) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci struct efx_nic *efx = channel->efx; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci channel->napi_dev = efx->net_dev; 132262306a36Sopenharmony_ci netif_napi_add(channel->napi_dev, &channel->napi_str, efx_poll); 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_civoid efx_siena_init_napi(struct efx_nic *efx) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci struct efx_channel *channel; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci efx_for_each_channel(channel, efx) 133062306a36Sopenharmony_ci efx_init_napi_channel(channel); 133162306a36Sopenharmony_ci} 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_cistatic void efx_fini_napi_channel(struct efx_channel *channel) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci if (channel->napi_dev) 133662306a36Sopenharmony_ci netif_napi_del(&channel->napi_str); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci channel->napi_dev = NULL; 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_civoid efx_siena_fini_napi(struct efx_nic *efx) 134262306a36Sopenharmony_ci{ 134362306a36Sopenharmony_ci struct efx_channel *channel; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci efx_for_each_channel(channel, efx) 134662306a36Sopenharmony_ci efx_fini_napi_channel(channel); 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci/*************** 135062306a36Sopenharmony_ci * Housekeeping 135162306a36Sopenharmony_ci ***************/ 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_cistatic int efx_channel_dummy_op_int(struct efx_channel *channel) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci return 0; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_civoid efx_siena_channel_dummy_op_void(struct efx_channel *channel) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cistatic const struct efx_channel_type efx_default_channel_type = { 136362306a36Sopenharmony_ci .pre_probe = efx_channel_dummy_op_int, 136462306a36Sopenharmony_ci .post_remove = efx_siena_channel_dummy_op_void, 136562306a36Sopenharmony_ci .get_name = efx_get_channel_name, 136662306a36Sopenharmony_ci .copy = efx_copy_channel, 136762306a36Sopenharmony_ci .want_txqs = efx_default_channel_want_txqs, 136862306a36Sopenharmony_ci .keep_eventq = false, 136962306a36Sopenharmony_ci .want_pio = true, 137062306a36Sopenharmony_ci}; 1371