// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2013 - 2018 Intel Corporation. */
#include <linux/etherdevice.h>
#include <linux/of_net.h>
#include <linux/pci.h>
#include <linux/bpf.h>
/* Local includes */
#include "i40e.h"
#include "i40e_diag.h"
#include "i40e_xsk.h"
#include <net/udp_tunnel.h>
#include <net/xdp_sock.h>
/* All i40e tracepoints are defined by the include below, which
* must be included exactly once across the whole kernel with
* CREATE_TRACE_POINTS defined
*/
#define CREATE_TRACE_POINTS
#include "i40e_trace.h"
const char i40e_driver_name[] = "i40e";
static const char i40e_driver_string[] =
"Intel(R) Ethernet Connection XL710 Network Driver";
#define DRV_KERN "-k"
#define DRV_VERSION_MAJOR 2
#define DRV_VERSION_MINOR 8
#define DRV_VERSION_BUILD 20
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) DRV_KERN
const char i40e_driver_version_str[] = DRV_VERSION;
static const char i40e_copyright[] = "Copyright (c) 2013 - 2019 Intel Corporation.";
/* a bit of forward declarations */
static void i40e_vsi_reinit_locked(struct i40e_vsi *vsi);
static void i40e_handle_reset_warning(struct i40e_pf *pf, bool lock_acquired);
static int i40e_add_vsi(struct i40e_vsi *vsi);
static int i40e_add_veb(struct i40e_veb *veb, struct i40e_vsi *vsi);
static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit);
static int i40e_setup_misc_vector(struct i40e_pf *pf);
static void i40e_determine_queue_usage(struct i40e_pf *pf);
static int i40e_setup_pf_filter_control(struct i40e_pf *pf);
static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired);
static int i40e_reset(struct i40e_pf *pf);
static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
static int i40e_setup_misc_vector_for_recovery_mode(struct i40e_pf *pf);
static int i40e_restore_interrupt_scheme(struct i40e_pf *pf);
static bool i40e_check_recovery_mode(struct i40e_pf *pf);
static int i40e_init_recovery_mode(struct i40e_pf *pf, struct i40e_hw *hw);
static void i40e_fdir_sb_setup(struct i40e_pf *pf);
static int i40e_veb_get_bw_info(struct i40e_veb *veb);
static int i40e_get_capabilities(struct i40e_pf *pf,
enum i40e_admin_queue_opc list_type);
/* i40e_pci_tbl - PCI Device ID Table
*
* Last entry must be all 0s
*
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
* Class, Class Mask, private data (not used) }
*/
static const struct pci_device_id i40e_pci_tbl[] = {
{PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_SFP), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_I_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_X710_N3000), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_XXV710_N3000), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_SFP28), 0},
/* required last entry */
{0, }
};
MODULE_DEVICE_TABLE(pci, i40e_pci_tbl);
#define I40E_MAX_VF_COUNT 128
static int debug = -1;
module_param(debug, uint, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all), Debug mask (0x8XXXXXXX)");
MODULE_AUTHOR("Intel Corporation, <e1000-devel@lists.sourceforge.net>");
MODULE_DESCRIPTION("Intel(R) Ethernet Connection XL710 Network Driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRV_VERSION);
static struct workqueue_struct *i40e_wq;
/**
* i40e_allocate_dma_mem_d - OS specific memory alloc for shared code
* @hw: pointer to the HW structure
* @mem: ptr to mem struct to fill out
* @size: size of memory requested
* @alignment: what to align the allocation to
**/
int i40e_allocate_dma_mem_d(struct i40e_hw *hw, struct i40e_dma_mem *mem,
u64 size, u32 alignment)
{
struct i40e_pf *pf = (struct i40e_pf *)hw->back;
mem->size = ALIGN(size, alignment);
mem->va = dma_alloc_coherent(&pf->pdev->dev, mem->size, &mem->pa,
GFP_KERNEL);
if (!mem->va)
return -ENOMEM;
return 0;
}
/**
* i40e_free_dma_mem_d - OS specific memory free for shared code
* @hw: pointer to the HW structure
* @mem: ptr to mem struct to free
**/
int i40e_free_dma_mem_d(struct i40e_hw *hw, struct i40e_dma_mem *mem)
{
struct i40e_pf *pf = (struct i40e_pf *)hw->back;
dma_free_coherent(&pf->pdev->dev, mem->size, mem->va, mem->pa);
mem->va = NULL;
mem->pa = 0;
mem->size = 0;
return 0;
}
/**
* i40e_allocate_virt_mem_d - OS specific memory alloc for shared code
* @hw: pointer to the HW structure
* @mem: ptr to mem struct to fill out
* @size: size of memory requested
**/
int i40e_allocate_virt_mem_d(struct i40e_hw *hw, struct i40e_virt_mem *mem,
u32 size)
{
mem->size = size;
mem->va = kzalloc(size, GFP_KERNEL);
if (!mem->va)
return -ENOMEM;
return 0;
}
/**
* i40e_free_virt_mem_d - OS specific memory free for shared code
* @hw: pointer to the HW structure
* @mem: ptr to mem struct to free
**/
int i40e_free_virt_mem_d(struct i40e_hw *hw, struct i40e_virt_mem *mem)
{
/* it's ok to kfree a NULL pointer */
kfree(mem->va);
mem->va = NULL;
mem->size = 0;
return 0;
}
/**
* i40e_get_lump - find a lump of free generic resource
* @pf: board private structure
* @pile: the pile of resource to search
* @needed: the number of items needed
* @id: an owner id to stick on the items assigned
*
* Returns the base item index of the lump, or negative for error
*
* The search_hint trick and lack of advanced fit-finding only work
* because we're highly likely to have all the same size lump requests.
* Linear search time and any fragmentation should be minimal.
**/
static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile,
u16 needed, u16 id)
{
int ret = -ENOMEM;
int i, j;
if (!pile || needed == 0 || id >= I40E_PILE_VALID_BIT) {
dev_info(&pf->pdev->dev,
"param err: pile=%s needed=%d id=0x%04x\n",
pile ? "<valid>" : "<null>", needed, id);
return -EINVAL;
}
/* start the linear search with an imperfect hint */
i = pile->search_hint;
while (i < pile->num_entries) {
/* skip already allocated entries */
if (pile->list[i] & I40E_PILE_VALID_BIT) {
i++;
continue;
}
/* do we have enough in this lump? */
for (j = 0; (j < needed) && ((i+j) < pile->num_entries); j++) {
if (pile->list[i+j] & I40E_PILE_VALID_BIT)
break;
}
if (j == needed) {
/* there was enough, so assign it to the requestor */
for (j = 0; j < needed; j++)
pile->list[i+j] = id | I40E_PILE_VALID_BIT;
ret = i;
pile->search_hint = i + j;
break;
}
/* not enough, so skip over it and continue looking */
i += j;
}
return ret;
}
/**
* i40e_put_lump - return a lump of generic resource
* @pile: the pile of resource to search
* @index: the base item index
* @id: the owner id of the items assigned
*
* Returns the count of items in the lump
**/
static int i40e_put_lump(struct i40e_lump_tracking *pile, u16 index, u16 id)
{
int valid_id = (id | I40E_PILE_VALID_BIT);
int count = 0;
int i;
if (!pile || index >= pile->num_entries)
return -EINVAL;
for (i = index;
i < pile->num_entries && pile->list[i] == valid_id;
i++) {
pile->list[i] = 0;
count++;
}
if (count && index < pile->search_hint)
pile->search_hint = index;
return count;
}
/**
* i40e_find_vsi_from_id - searches for the vsi with the given id
* @pf: the pf structure to search for the vsi
* @id: id of the vsi it is searching for
**/
struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id)
{
int i;
for (i = 0; i < pf->num_alloc_vsi; i++)
if (pf->vsi[i] && (pf->vsi[i]->id == id))
return pf->vsi[i];
return NULL;
}
/**
* i40e_service_event_schedule - Schedule the service task to wake up
* @pf: board private structure
*
* If not already scheduled, this puts the task into the work queue
**/
void i40e_service_event_schedule(struct i40e_pf *pf)
{
if ((!test_bit(__I40E_DOWN, pf->state) &&
!test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) ||
test_bit(__I40E_RECOVERY_MODE, pf->state))
queue_work(i40e_wq, &pf->service_task);
}
/**
* i40e_tx_timeout - Respond to a Tx Hang
* @netdev: network interface device structure
*
* If any port has noticed a Tx timeout, it is likely that the whole
* device is munged, not just the one netdev port, so go for the full
* reset.
**/
static void i40e_tx_timeout(struct net_device *netdev)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
struct i40e_ring *tx_ring = NULL;
unsigned int i, hung_queue = 0;
u32 head, val;
pf->tx_timeout_count++;
/* find the stopped queue the same way the stack does */
for (i = 0; i < netdev->num_tx_queues; i++) {
struct netdev_queue *q;
unsigned long trans_start;
q = netdev_get_tx_queue(netdev, i);
trans_start = q->trans_start;
if (netif_xmit_stopped(q) &&
time_after(jiffies,
(trans_start + netdev->watchdog_timeo))) {
hung_queue = i;
break;
}
}
if (i == netdev->num_tx_queues) {
netdev_info(netdev, "tx_timeout: no netdev hung queue found\n");
} else {
/* now that we have an index, find the tx_ring struct */
for (i = 0; i < vsi->num_queue_pairs; i++) {
if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) {
if (hung_queue ==
vsi->tx_rings[i]->queue_index) {
tx_ring = vsi->tx_rings[i];
break;
}
}
}
}
if (time_after(jiffies, (pf->tx_timeout_last_recovery + HZ*20)))
pf->tx_timeout_recovery_level = 1; /* reset after some time */
else if (time_before(jiffies,
(pf->tx_timeout_last_recovery + netdev->watchdog_timeo)))
return; /* don't do any new action before the next timeout */
/* don't kick off another recovery if one is already pending */
if (test_and_set_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state))
return;
if (tx_ring) {
head = i40e_get_head(tx_ring);
/* Read interrupt register */
if (pf->flags & I40E_FLAG_MSIX_ENABLED)
val = rd32(&pf->hw,
I40E_PFINT_DYN_CTLN(tx_ring->q_vector->v_idx +
tx_ring->vsi->base_vector - 1));
else
val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0);
netdev_info(netdev, "tx_timeout: VSI_seid: %d, Q %d, NTC: 0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x, INT: 0x%x\n",
vsi->seid, hung_queue, tx_ring->next_to_clean,
head, tx_ring->next_to_use,
readl(tx_ring->tail), val);
}
pf->tx_timeout_last_recovery = jiffies;
netdev_info(netdev, "tx_timeout recovery level %d, hung_queue %d\n",
pf->tx_timeout_recovery_level, hung_queue);
switch (pf->tx_timeout_recovery_level) {
case 1:
set_bit(__I40E_PF_RESET_REQUESTED, pf->state);
break;
case 2:
set_bit(__I40E_CORE_RESET_REQUESTED, pf->state);
break;
case 3:
set_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state);
break;
default:
netdev_err(netdev, "tx_timeout recovery unsuccessful\n");
break;
}
i40e_service_event_schedule(pf);
pf->tx_timeout_recovery_level++;
}
/**
* i40e_get_vsi_stats_struct - Get System Network Statistics
* @vsi: the VSI we care about
*
* Returns the address of the device statistics structure.
* The statistics are actually updated from the service task.
**/
struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi)
{
return &vsi->net_stats;
}
/**
* i40e_get_netdev_stats_struct_tx - populate stats from a Tx ring
* @ring: Tx ring to get statistics from
* @stats: statistics entry to be updated
**/
static void i40e_get_netdev_stats_struct_tx(struct i40e_ring *ring,
struct rtnl_link_stats64 *stats)
{
u64 bytes, packets;
unsigned int start;
do {
start = u64_stats_fetch_begin_irq(&ring->syncp);
packets = ring->stats.packets;
bytes = ring->stats.bytes;
} while (u64_stats_fetch_retry_irq(&ring->syncp, start));
stats->tx_packets += packets;
stats->tx_bytes += bytes;
}
/**
* i40e_get_netdev_stats_struct - Get statistics for netdev interface
* @netdev: network interface device structure
* @stats: data structure to store statistics
*
* Returns the address of the device statistics structure.
* The statistics are actually updated from the service task.
**/
static void i40e_get_netdev_stats_struct(struct net_device *netdev,
struct rtnl_link_stats64 *stats)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi);
struct i40e_ring *ring;
int i;
if (test_bit(__I40E_VSI_DOWN, vsi->state))
return;
if (!vsi->tx_rings)
return;
rcu_read_lock();
for (i = 0; i < vsi->num_queue_pairs; i++) {
u64 bytes, packets;
unsigned int start;
ring = READ_ONCE(vsi->tx_rings[i]);
if (!ring)
continue;
i40e_get_netdev_stats_struct_tx(ring, stats);
if (i40e_enabled_xdp_vsi(vsi)) {
ring++;
i40e_get_netdev_stats_struct_tx(ring, stats);
}
ring++;
do {
start = u64_stats_fetch_begin_irq(&ring->syncp);
packets = ring->stats.packets;
bytes = ring->stats.bytes;
} while (u64_stats_fetch_retry_irq(&ring->syncp, start));
stats->rx_packets += packets;
stats->rx_bytes += bytes;
}
rcu_read_unlock();
/* following stats updated by i40e_watchdog_subtask() */
stats->multicast = vsi_stats->multicast;
stats->tx_errors = vsi_stats->tx_errors;
stats->tx_dropped = vsi_stats->tx_dropped;
stats->rx_errors = vsi_stats->rx_errors;
stats->rx_dropped = vsi_stats->rx_dropped;
stats->rx_crc_errors = vsi_stats->rx_crc_errors;
stats->rx_length_errors = vsi_stats->rx_length_errors;
}
/**
* i40e_vsi_reset_stats - Resets all stats of the given vsi
* @vsi: the VSI to have its stats reset
**/
void i40e_vsi_reset_stats(struct i40e_vsi *vsi)
{
struct rtnl_link_stats64 *ns;
int i;
if (!vsi)
return;
ns = i40e_get_vsi_stats_struct(vsi);
memset(ns, 0, sizeof(*ns));
memset(&vsi->net_stats_offsets, 0, sizeof(vsi->net_stats_offsets));
memset(&vsi->eth_stats, 0, sizeof(vsi->eth_stats));
memset(&vsi->eth_stats_offsets, 0, sizeof(vsi->eth_stats_offsets));
if (vsi->rx_rings && vsi->rx_rings[0]) {
for (i = 0; i < vsi->num_queue_pairs; i++) {
memset(&vsi->rx_rings[i]->stats, 0,
sizeof(vsi->rx_rings[i]->stats));
memset(&vsi->rx_rings[i]->rx_stats, 0,
sizeof(vsi->rx_rings[i]->rx_stats));
memset(&vsi->tx_rings[i]->stats, 0,
sizeof(vsi->tx_rings[i]->stats));
memset(&vsi->tx_rings[i]->tx_stats, 0,
sizeof(vsi->tx_rings[i]->tx_stats));
}
}
vsi->stat_offsets_loaded = false;
}
/**
* i40e_pf_reset_stats - Reset all of the stats for the given PF
* @pf: the PF to be reset
**/
void i40e_pf_reset_stats(struct i40e_pf *pf)
{
int i;
memset(&pf->stats, 0, sizeof(pf->stats));
memset(&pf->stats_offsets, 0, sizeof(pf->stats_offsets));
pf->stat_offsets_loaded = false;
for (i = 0; i < I40E_MAX_VEB; i++) {
if (pf->veb[i]) {
memset(&pf->veb[i]->stats, 0,
sizeof(pf->veb[i]->stats));
memset(&pf->veb[i]->stats_offsets, 0,
sizeof(pf->veb[i]->stats_offsets));
pf->veb[i]->stat_offsets_loaded = false;
}
}
pf->hw_csum_rx_error = 0;
}
/**
* i40e_stat_update48 - read and update a 48 bit stat from the chip
* @hw: ptr to the hardware info
* @hireg: the high 32 bit reg to read
* @loreg: the low 32 bit reg to read
* @offset_loaded: has the initial offset been loaded yet
* @offset: ptr to current offset value
* @stat: ptr to the stat
*
* Since the device stats are not reset at PFReset, they likely will not
* be zeroed when the driver starts. We'll save the first values read
* and use them as offsets to be subtracted from the raw values in order
* to report stats that count from zero. In the process, we also manage
* the potential roll-over.
**/
static void i40e_stat_update48(struct i40e_hw *hw, u32 hireg, u32 loreg,
bool offset_loaded, u64 *offset, u64 *stat)
{
u64 new_data;
if (hw->device_id == I40E_DEV_ID_QEMU) {
new_data = rd32(hw, loreg);
new_data |= ((u64)(rd32(hw, hireg) & 0xFFFF)) << 32;
} else {
new_data = rd64(hw, loreg);
}
if (!offset_loaded)
*offset = new_data;
if (likely(new_data >= *offset))
*stat = new_data - *offset;
else
*stat = (new_data + BIT_ULL(48)) - *offset;
*stat &= 0xFFFFFFFFFFFFULL;
}
/**
* i40e_stat_update32 - read and update a 32 bit stat from the chip
* @hw: ptr to the hardware info
* @reg: the hw reg to read
* @offset_loaded: has the initial offset been loaded yet
* @offset: ptr to current offset value
* @stat: ptr to the stat
**/
static void i40e_stat_update32(struct i40e_hw *hw, u32 reg,
bool offset_loaded, u64 *offset, u64 *stat)
{
u32 new_data;
new_data = rd32(hw, reg);
if (!offset_loaded)
*offset = new_data;
if (likely(new_data >= *offset))
*stat = (u32)(new_data - *offset);
else
*stat = (u32)((new_data + BIT_ULL(32)) - *offset);
}
/**
* i40e_stat_update_and_clear32 - read and clear hw reg, update a 32 bit stat
* @hw: ptr to the hardware info
* @reg: the hw reg to read and clear
* @stat: ptr to the stat
**/
static void i40e_stat_update_and_clear32(struct i40e_hw *hw, u32 reg, u64 *stat)
{
u32 new_data = rd32(hw, reg);
wr32(hw, reg, 1); /* must write a nonzero value to clear register */
*stat += new_data;
}
/**
* i40e_update_eth_stats - Update VSI-specific ethernet statistics counters.
* @vsi: the VSI to be updated
**/
void i40e_update_eth_stats(struct i40e_vsi *vsi)
{
int stat_idx = le16_to_cpu(vsi->info.stat_counter_idx);
struct i40e_pf *pf = vsi->back;
struct i40e_hw *hw = &pf->hw;
struct i40e_eth_stats *oes;
struct i40e_eth_stats *es; /* device's eth stats */
es = &vsi->eth_stats;
oes = &vsi->eth_stats_offsets;
/* Gather up the stats that the hw collects */
i40e_stat_update32(hw, I40E_GLV_TEPC(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_errors, &es->tx_errors);
i40e_stat_update32(hw, I40E_GLV_RDPC(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_discards, &es->rx_discards);
i40e_stat_update32(hw, I40E_GLV_RUPP(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_unknown_protocol, &es->rx_unknown_protocol);
i40e_stat_update48(hw, I40E_GLV_GORCH(stat_idx),
I40E_GLV_GORCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_bytes, &es->rx_bytes);
i40e_stat_update48(hw, I40E_GLV_UPRCH(stat_idx),
I40E_GLV_UPRCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_unicast, &es->rx_unicast);
i40e_stat_update48(hw, I40E_GLV_MPRCH(stat_idx),
I40E_GLV_MPRCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_multicast, &es->rx_multicast);
i40e_stat_update48(hw, I40E_GLV_BPRCH(stat_idx),
I40E_GLV_BPRCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_broadcast, &es->rx_broadcast);
i40e_stat_update48(hw, I40E_GLV_GOTCH(stat_idx),
I40E_GLV_GOTCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_bytes, &es->tx_bytes);
i40e_stat_update48(hw, I40E_GLV_UPTCH(stat_idx),
I40E_GLV_UPTCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_unicast, &es->tx_unicast);
i40e_stat_update48(hw, I40E_GLV_MPTCH(stat_idx),
I40E_GLV_MPTCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_multicast, &es->tx_multicast);
i40e_stat_update48(hw, I40E_GLV_BPTCH(stat_idx),
I40E_GLV_BPTCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_broadcast, &es->tx_broadcast);
vsi->stat_offsets_loaded = true;
}
/**
* i40e_update_veb_stats - Update Switch component statistics
* @veb: the VEB being updated
**/
void i40e_update_veb_stats(struct i40e_veb *veb)
{
struct i40e_pf *pf = veb->pf;
struct i40e_hw *hw = &pf->hw;
struct i40e_eth_stats *oes;
struct i40e_eth_stats *es; /* device's eth stats */
struct i40e_veb_tc_stats *veb_oes;
struct i40e_veb_tc_stats *veb_es;
int i, idx = 0;
idx = veb->stats_idx;
es = &veb->stats;
oes = &veb->stats_offsets;
veb_es = &veb->tc_stats;
veb_oes = &veb->tc_stats_offsets;
/* Gather up the stats that the hw collects */
i40e_stat_update32(hw, I40E_GLSW_TDPC(idx),
veb->stat_offsets_loaded,
&oes->tx_discards, &es->tx_discards);
if (hw->revision_id > 0)
i40e_stat_update32(hw, I40E_GLSW_RUPP(idx),
veb->stat_offsets_loaded,
&oes->rx_unknown_protocol,
&es->rx_unknown_protocol);
i40e_stat_update48(hw, I40E_GLSW_GORCH(idx), I40E_GLSW_GORCL(idx),
veb->stat_offsets_loaded,
&oes->rx_bytes, &es->rx_bytes);
i40e_stat_update48(hw, I40E_GLSW_UPRCH(idx), I40E_GLSW_UPRCL(idx),
veb->stat_offsets_loaded,
&oes->rx_unicast, &es->rx_unicast);
i40e_stat_update48(hw, I40E_GLSW_MPRCH(idx), I40E_GLSW_MPRCL(idx),
veb->stat_offsets_loaded,
&oes->rx_multicast, &es->rx_multicast);
i40e_stat_update48(hw, I40E_GLSW_BPRCH(idx), I40E_GLSW_BPRCL(idx),
veb->stat_offsets_loaded,
&oes->rx_broadcast, &es->rx_broadcast);
i40e_stat_update48(hw, I40E_GLSW_GOTCH(idx), I40E_GLSW_GOTCL(idx),
veb->stat_offsets_loaded,
&oes->tx_bytes, &es->tx_bytes);
i40e_stat_update48(hw, I40E_GLSW_UPTCH(idx), I40E_GLSW_UPTCL(idx),
veb->stat_offsets_loaded,
&oes->tx_unicast, &es->tx_unicast);
i40e_stat_update48(hw, I40E_GLSW_MPTCH(idx), I40E_GLSW_MPTCL(idx),
veb->stat_offsets_loaded,
&oes->tx_multicast, &es->tx_multicast);
i40e_stat_update48(hw, I40E_GLSW_BPTCH(idx), I40E_GLSW_BPTCL(idx),
veb->stat_offsets_loaded,
&oes->tx_broadcast, &es->tx_broadcast);
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
i40e_stat_update48(hw, I40E_GLVEBTC_RPCH(i, idx),
I40E_GLVEBTC_RPCL(i, idx),
veb->stat_offsets_loaded,
&veb_oes->tc_rx_packets[i],
&veb_es->tc_rx_packets[i]);
i40e_stat_update48(hw, I40E_GLVEBTC_RBCH(i, idx),
I40E_GLVEBTC_RBCL(i, idx),
veb->stat_offsets_loaded,
&veb_oes->tc_rx_bytes[i],
&veb_es->tc_rx_bytes[i]);
i40e_stat_update48(hw, I40E_GLVEBTC_TPCH(i, idx),
I40E_GLVEBTC_TPCL(i, idx),
veb->stat_offsets_loaded,
&veb_oes->tc_tx_packets[i],
&veb_es->tc_tx_packets[i]);
i40e_stat_update48(hw, I40E_GLVEBTC_TBCH(i, idx),
I40E_GLVEBTC_TBCL(i, idx),
veb->stat_offsets_loaded,
&veb_oes->tc_tx_bytes[i],
&veb_es->tc_tx_bytes[i]);
}
veb->stat_offsets_loaded = true;
}
/**
* i40e_update_vsi_stats - Update the vsi statistics counters.
* @vsi: the VSI to be updated
*
* There are a few instances where we store the same stat in a
* couple of different structs. This is partly because we have
* the netdev stats that need to be filled out, which is slightly
* different from the "eth_stats" defined by the chip and used in
* VF communications. We sort it out here.
**/
static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
{
struct i40e_pf *pf = vsi->back;
struct rtnl_link_stats64 *ons;
struct rtnl_link_stats64 *ns; /* netdev stats */
struct i40e_eth_stats *oes;
struct i40e_eth_stats *es; /* device's eth stats */
u32 tx_restart, tx_busy;
struct i40e_ring *p;
u32 rx_page, rx_buf;
u64 bytes, packets;
unsigned int start;
u64 tx_linearize;
u64 tx_force_wb;
u64 rx_p, rx_b;
u64 tx_p, tx_b;
u16 q;
if (test_bit(__I40E_VSI_DOWN, vsi->state) ||
test_bit(__I40E_CONFIG_BUSY, pf->state))
return;
ns = i40e_get_vsi_stats_struct(vsi);
ons = &vsi->net_stats_offsets;
es = &vsi->eth_stats;
oes = &vsi->eth_stats_offsets;
/* Gather up the netdev and vsi stats that the driver collects
* on the fly during packet processing
*/
rx_b = rx_p = 0;
tx_b = tx_p = 0;
tx_restart = tx_busy = tx_linearize = tx_force_wb = 0;
rx_page = 0;
rx_buf = 0;
rcu_read_lock();
for (q = 0; q < vsi->num_queue_pairs; q++) {
/* locate Tx ring */
p = READ_ONCE(vsi->tx_rings[q]);
do {
start = u64_stats_fetch_begin_irq(&p->syncp);
packets = p->stats.packets;
bytes = p->stats.bytes;
} while (u64_stats_fetch_retry_irq(&p->syncp, start));
tx_b += bytes;
tx_p += packets;
tx_restart += p->tx_stats.restart_queue;
tx_busy += p->tx_stats.tx_busy;
tx_linearize += p->tx_stats.tx_linearize;
tx_force_wb += p->tx_stats.tx_force_wb;
/* Rx queue is part of the same block as Tx queue */
p = &p[1];
do {
start = u64_stats_fetch_begin_irq(&p->syncp);
packets = p->stats.packets;
bytes = p->stats.bytes;
} while (u64_stats_fetch_retry_irq(&p->syncp, start));
rx_b += bytes;
rx_p += packets;
rx_buf += p->rx_stats.alloc_buff_failed;
rx_page += p->rx_stats.alloc_page_failed;
}
rcu_read_unlock();
vsi->tx_restart = tx_restart;
vsi->tx_busy = tx_busy;
vsi->tx_linearize = tx_linearize;
vsi->tx_force_wb = tx_force_wb;
vsi->rx_page_failed = rx_page;
vsi->rx_buf_failed = rx_buf;
ns->rx_packets = rx_p;
ns->rx_bytes = rx_b;
ns->tx_packets = tx_p;
ns->tx_bytes = tx_b;
/* update netdev stats from eth stats */
i40e_update_eth_stats(vsi);
ons->tx_errors = oes->tx_errors;
ns->tx_errors = es->tx_errors;
ons->multicast = oes->rx_multicast;
ns->multicast = es->rx_multicast;
ons->rx_dropped = oes->rx_discards;
ns->rx_dropped = es->rx_discards;
ons->tx_dropped = oes->tx_discards;
ns->tx_dropped = es->tx_discards;
/* pull in a couple PF stats if this is the main vsi */
if (vsi == pf->vsi[pf->lan_vsi]) {
ns->rx_crc_errors = pf->stats.crc_errors;
ns->rx_errors = pf->stats.crc_errors + pf->stats.illegal_bytes;
ns->rx_length_errors = pf->stats.rx_length_errors;
}
}
/**
* i40e_update_pf_stats - Update the PF statistics counters.
* @pf: the PF to be updated
**/
static void i40e_update_pf_stats(struct i40e_pf *pf)
{
struct i40e_hw_port_stats *osd = &pf->stats_offsets;
struct i40e_hw_port_stats *nsd = &pf->stats;
struct i40e_hw *hw = &pf->hw;
u32 val;
int i;
i40e_stat_update48(hw, I40E_GLPRT_GORCH(hw->port),
I40E_GLPRT_GORCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_bytes, &nsd->eth.rx_bytes);
i40e_stat_update48(hw, I40E_GLPRT_GOTCH(hw->port),
I40E_GLPRT_GOTCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.tx_bytes, &nsd->eth.tx_bytes);
i40e_stat_update32(hw, I40E_GLPRT_RDPC(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_discards,
&nsd->eth.rx_discards);
i40e_stat_update48(hw, I40E_GLPRT_UPRCH(hw->port),
I40E_GLPRT_UPRCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_unicast,
&nsd->eth.rx_unicast);
i40e_stat_update48(hw, I40E_GLPRT_MPRCH(hw->port),
I40E_GLPRT_MPRCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_multicast,
&nsd->eth.rx_multicast);
i40e_stat_update48(hw, I40E_GLPRT_BPRCH(hw->port),
I40E_GLPRT_BPRCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_broadcast,
&nsd->eth.rx_broadcast);
i40e_stat_update48(hw, I40E_GLPRT_UPTCH(hw->port),
I40E_GLPRT_UPTCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.tx_unicast,
&nsd->eth.tx_unicast);
i40e_stat_update48(hw, I40E_GLPRT_MPTCH(hw->port),
I40E_GLPRT_MPTCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.tx_multicast,
&nsd->eth.tx_multicast);
i40e_stat_update48(hw, I40E_GLPRT_BPTCH(hw->port),
I40E_GLPRT_BPTCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.tx_broadcast,
&nsd->eth.tx_broadcast);
i40e_stat_update32(hw, I40E_GLPRT_TDOLD(hw->port),
pf->stat_offsets_loaded,
&osd->tx_dropped_link_down,
&nsd->tx_dropped_link_down);
i40e_stat_update32(hw, I40E_GLPRT_CRCERRS(hw->port),
pf->stat_offsets_loaded,
&osd->crc_errors, &nsd->crc_errors);
i40e_stat_update32(hw, I40E_GLPRT_ILLERRC(hw->port),
pf->stat_offsets_loaded,
&osd->illegal_bytes, &nsd->illegal_bytes);
i40e_stat_update32(hw, I40E_GLPRT_MLFC(hw->port),
pf->stat_offsets_loaded,
&osd->mac_local_faults,
&nsd->mac_local_faults);
i40e_stat_update32(hw, I40E_GLPRT_MRFC(hw->port),
pf->stat_offsets_loaded,
&osd->mac_remote_faults,
&nsd->mac_remote_faults);
i40e_stat_update32(hw, I40E_GLPRT_RLEC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_length_errors,
&nsd->rx_length_errors);
i40e_stat_update32(hw, I40E_GLPRT_LXONRXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xon_rx, &nsd->link_xon_rx);
i40e_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xon_tx, &nsd->link_xon_tx);
i40e_stat_update32(hw, I40E_GLPRT_LXOFFRXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xoff_rx, &nsd->link_xoff_rx);
i40e_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xoff_tx, &nsd->link_xoff_tx);
for (i = 0; i < 8; i++) {
i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i),
pf->stat_offsets_loaded,
&osd->priority_xoff_rx[i],
&nsd->priority_xoff_rx[i]);
i40e_stat_update32(hw, I40E_GLPRT_PXONRXC(hw->port, i),
pf->stat_offsets_loaded,
&osd->priority_xon_rx[i],
&nsd->priority_xon_rx[i]);
i40e_stat_update32(hw, I40E_GLPRT_PXONTXC(hw->port, i),
pf->stat_offsets_loaded,
&osd->priority_xon_tx[i],
&nsd->priority_xon_tx[i]);
i40e_stat_update32(hw, I40E_GLPRT_PXOFFTXC(hw->port, i),
pf->stat_offsets_loaded,
&osd->priority_xoff_tx[i],
&nsd->priority_xoff_tx[i]);
i40e_stat_update32(hw,
I40E_GLPRT_RXON2OFFCNT(hw->port, i),
pf->stat_offsets_loaded,
&osd->priority_xon_2_xoff[i],
&nsd->priority_xon_2_xoff[i]);
}
i40e_stat_update48(hw, I40E_GLPRT_PRC64H(hw->port),
I40E_GLPRT_PRC64L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_64, &nsd->rx_size_64);
i40e_stat_update48(hw, I40E_GLPRT_PRC127H(hw->port),
I40E_GLPRT_PRC127L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_127, &nsd->rx_size_127);
i40e_stat_update48(hw, I40E_GLPRT_PRC255H(hw->port),
I40E_GLPRT_PRC255L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_255, &nsd->rx_size_255);
i40e_stat_update48(hw, I40E_GLPRT_PRC511H(hw->port),
I40E_GLPRT_PRC511L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_511, &nsd->rx_size_511);
i40e_stat_update48(hw, I40E_GLPRT_PRC1023H(hw->port),
I40E_GLPRT_PRC1023L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_1023, &nsd->rx_size_1023);
i40e_stat_update48(hw, I40E_GLPRT_PRC1522H(hw->port),
I40E_GLPRT_PRC1522L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_1522, &nsd->rx_size_1522);
i40e_stat_update48(hw, I40E_GLPRT_PRC9522H(hw->port),
I40E_GLPRT_PRC9522L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_big, &nsd->rx_size_big);
i40e_stat_update48(hw, I40E_GLPRT_PTC64H(hw->port),
I40E_GLPRT_PTC64L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_64, &nsd->tx_size_64);
i40e_stat_update48(hw, I40E_GLPRT_PTC127H(hw->port),
I40E_GLPRT_PTC127L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_127, &nsd->tx_size_127);
i40e_stat_update48(hw, I40E_GLPRT_PTC255H(hw->port),
I40E_GLPRT_PTC255L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_255, &nsd->tx_size_255);
i40e_stat_update48(hw, I40E_GLPRT_PTC511H(hw->port),
I40E_GLPRT_PTC511L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_511, &nsd->tx_size_511);
i40e_stat_update48(hw, I40E_GLPRT_PTC1023H(hw->port),
I40E_GLPRT_PTC1023L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_1023, &nsd->tx_size_1023);
i40e_stat_update48(hw, I40E_GLPRT_PTC1522H(hw->port),
I40E_GLPRT_PTC1522L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_1522, &nsd->tx_size_1522);
i40e_stat_update48(hw, I40E_GLPRT_PTC9522H(hw->port),
I40E_GLPRT_PTC9522L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_big, &nsd->tx_size_big);
i40e_stat_update32(hw, I40E_GLPRT_RUC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_undersize, &nsd->rx_undersize);
i40e_stat_update32(hw, I40E_GLPRT_RFC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_fragments, &nsd->rx_fragments);
i40e_stat_update32(hw, I40E_GLPRT_ROC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_oversize, &nsd->rx_oversize);
i40e_stat_update32(hw, I40E_GLPRT_RJC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_jabber, &nsd->rx_jabber);
/* FDIR stats */
i40e_stat_update_and_clear32(hw,
I40E_GLQF_PCNT(I40E_FD_ATR_STAT_IDX(hw->pf_id)),
&nsd->fd_atr_match);
i40e_stat_update_and_clear32(hw,
I40E_GLQF_PCNT(I40E_FD_SB_STAT_IDX(hw->pf_id)),
&nsd->fd_sb_match);
i40e_stat_update_and_clear32(hw,
I40E_GLQF_PCNT(I40E_FD_ATR_TUNNEL_STAT_IDX(hw->pf_id)),
&nsd->fd_atr_tunnel_match);
val = rd32(hw, I40E_PRTPM_EEE_STAT);
nsd->tx_lpi_status =
(val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >>
I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT;
nsd->rx_lpi_status =
(val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >>
I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT;
i40e_stat_update32(hw, I40E_PRTPM_TLPIC,
pf->stat_offsets_loaded,
&osd->tx_lpi_count, &nsd->tx_lpi_count);
i40e_stat_update32(hw, I40E_PRTPM_RLPIC,
pf->stat_offsets_loaded,
&osd->rx_lpi_count, &nsd->rx_lpi_count);
if (pf->flags & I40E_FLAG_FD_SB_ENABLED &&
!test_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state))
nsd->fd_sb_status = true;
else
nsd->fd_sb_status = false;
if (pf->flags & I40E_FLAG_FD_ATR_ENABLED &&
!test_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state))
nsd->fd_atr_status = true;
else
nsd->fd_atr_status = false;
pf->stat_offsets_loaded = true;
}
/**
* i40e_update_stats - Update the various statistics counters.
* @vsi: the VSI to be updated
*
* Update the various stats for this VSI and its related entities.
**/
void i40e_update_stats(struct i40e_vsi *vsi)
{
struct i40e_pf *pf = vsi->back;
if (vsi == pf->vsi[pf->lan_vsi])
i40e_update_pf_stats(pf);
i40e_update_vsi_stats(vsi);
}
/**
* i40e_find_filter - Search VSI filter list for specific mac/vlan filter
* @vsi: the VSI to be searched
* @macaddr: the MAC address
* @vlan: the vlan
*
* Returns ptr to the filter object or NULL
**/
static struct i40e_mac_filter *i40e_find_filter(struct i40e_vsi *vsi,
const u8 *macaddr, s16 vlan)
{
struct i40e_mac_filter *f;
u64 key;
if (!vsi || !macaddr)
return NULL;
key = i40e_addr_to_hkey(macaddr);
hash_for_each_possible(vsi->mac_filter_hash, f, hlist, key) {
if ((ether_addr_equal(macaddr, f->macaddr)) &&
(vlan == f->vlan))
return f;
}
return NULL;
}
/**
* i40e_find_mac - Find a mac addr in the macvlan filters list
* @vsi: the VSI to be searched
* @macaddr: the MAC address we are searching for
*
* Returns the first filter with the provided MAC address or NULL if
* MAC address was not found
**/
struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr)
{
struct i40e_mac_filter *f;
u64 key;
if (!vsi || !macaddr)
return NULL;
key = i40e_addr_to_hkey(macaddr);
hash_for_each_possible(vsi->mac_filter_hash, f, hlist, key) {
if ((ether_addr_equal(macaddr, f->macaddr)))
return f;
}
return NULL;
}
/**
* i40e_is_vsi_in_vlan - Check if VSI is in vlan mode
* @vsi: the VSI to be searched
*
* Returns true if VSI is in vlan mode or false otherwise
**/
bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi)
{
/* If we have a PVID, always operate in VLAN mode */
if (vsi->info.pvid)
return true;
/* We need to operate in VLAN mode whenever we have any filters with
* a VLAN other than I40E_VLAN_ALL. We could check the table each
* time, incurring search cost repeatedly. However, we can notice two
* things:
*
* 1) the only place where we can gain a VLAN filter is in
* i40e_add_filter.
*
* 2) the only place where filters are actually removed is in
* i40e_sync_filters_subtask.
*
* Thus, we can simply use a boolean value, has_vlan_filters which we
* will set to true when we add a VLAN filter in i40e_add_filter. Then
* we have to perform the full search after deleting filters in
* i40e_sync_filters_subtask, but we already have to search
* filters here and can perform the check at the same time. This
* results in avoiding embedding a loop for VLAN mode inside another
* loop over all the filters, and should maintain correctness as noted
* above.
*/
return vsi->has_vlan_filter;
}
/**
* i40e_correct_mac_vlan_filters - Correct non-VLAN filters if necessary
* @vsi: the VSI to configure
* @tmp_add_list: list of filters ready to be added
* @tmp_del_list: list of filters ready to be deleted
* @vlan_filters: the number of active VLAN filters
*
* Update VLAN=0 and VLAN=-1 (I40E_VLAN_ANY) filters properly so that they
* behave as expected. If we have any active VLAN filters remaining or about
* to be added then we need to update non-VLAN filters to be marked as VLAN=0
* so that they only match against untagged traffic. If we no longer have any
* active VLAN filters, we need to make all non-VLAN filters marked as VLAN=-1
* so that they match against both tagged and untagged traffic. In this way,
* we ensure that we correctly receive the desired traffic. This ensures that
* when we have an active VLAN we will receive only untagged traffic and
* traffic matching active VLANs. If we have no active VLANs then we will
* operate in non-VLAN mode and receive all traffic, tagged or untagged.
*
* Finally, in a similar fashion, this function also corrects filters when
* there is an active PVID assigned to this VSI.
*
* In case of memory allocation failure return -ENOMEM. Otherwise, return 0.
*
* This function is only expected to be called from within
* i40e_sync_vsi_filters.
*
* NOTE: This function expects to be called while under the
* mac_filter_hash_lock
*/
static int i40e_correct_mac_vlan_filters(struct i40e_vsi *vsi,
struct hlist_head *tmp_add_list,
struct hlist_head *tmp_del_list,
int vlan_filters)
{
s16 pvid = le16_to_cpu(vsi->info.pvid);
struct i40e_mac_filter *f, *add_head;
struct i40e_new_mac_filter *new;
struct hlist_node *h;
int bkt, new_vlan;
/* To determine if a particular filter needs to be replaced we
* have the three following conditions:
*
* a) if we have a PVID assigned, then all filters which are
* not marked as VLAN=PVID must be replaced with filters that
* are.
* b) otherwise, if we have any active VLANS, all filters
* which are marked as VLAN=-1 must be replaced with
* filters marked as VLAN=0
* c) finally, if we do not have any active VLANS, all filters
* which are marked as VLAN=0 must be replaced with filters
* marked as VLAN=-1
*/
/* Update the filters about to be added in place */
hlist_for_each_entry(new, tmp_add_list, hlist) {
if (pvid && new->f->vlan != pvid)
new->f->vlan = pvid;
else if (vlan_filters && new->f->vlan == I40E_VLAN_ANY)
new->f->vlan = 0;
else if (!vlan_filters && new->f->vlan == 0)
new->f->vlan = I40E_VLAN_ANY;
}
/* Update the remaining active filters */
hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
/* Combine the checks for whether a filter needs to be changed
* and then determine the new VLAN inside the if block, in
* order to avoid duplicating code for adding the new filter
* then deleting the old filter.
*/
if ((pvid && f->vlan != pvid) ||
(vlan_filters && f->vlan == I40E_VLAN_ANY) ||
(!vlan_filters && f->vlan == 0)) {
/* Determine the new vlan we will be adding */
if (pvid)
new_vlan = pvid;
else if (vlan_filters)
new_vlan = 0;
else
new_vlan = I40E_VLAN_ANY;
/* Create the new filter */
add_head = i40e_add_filter(vsi, f->macaddr, new_vlan);
if (!add_head)
return -ENOMEM;
/* Create a temporary i40e_new_mac_filter */
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (!new)
return -ENOMEM;
new->f = add_head;
new->state = add_head->state;
/* Add the new filter to the tmp list */
hlist_add_head(&new->hlist, tmp_add_list);
/* Put the original filter into the delete list */
f->state = I40E_FILTER_REMOVE;
hash_del(&f->hlist);
hlist_add_head(&f->hlist, tmp_del_list);
}
}
vsi->has_vlan_filter = !!vlan_filters;
return 0;
}
/**
* i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM
* @vsi: the PF Main VSI - inappropriate for any other VSI
* @macaddr: the MAC address
*
* Remove whatever filter the firmware set up so the driver can manage
* its own filtering intelligently.
**/
static void i40e_rm_default_mac_filter(struct i40e_vsi *vsi, u8 *macaddr)
{
struct i40e_aqc_remove_macvlan_element_data element;
struct i40e_pf *pf = vsi->back;
/* Only appropriate for the PF main VSI */
if (vsi->type != I40E_VSI_MAIN)
return;
memset(&element, 0, sizeof(element));
ether_addr_copy(element.mac_addr, macaddr);
element.vlan_tag = 0;
/* Ignore error returns, some firmware does it this way... */
element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH;
i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL);
memset(&element, 0, sizeof(element));
ether_addr_copy(element.mac_addr, macaddr);
element.vlan_tag = 0;
/* ...and some firmware does it this way. */
element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL);
}
/**
* i40e_add_filter - Add a mac/vlan filter to the VSI
* @vsi: the VSI to be searched
* @macaddr: the MAC address
* @vlan: the vlan
*
* Returns ptr to the filter object or NULL when no memory available.
*
* NOTE: This function is expected to be called with mac_filter_hash_lock
* being held.
**/
struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
const u8 *macaddr, s16 vlan)
{
struct i40e_mac_filter *f;
u64 key;
if (!vsi || !macaddr)
return NULL;
f = i40e_find_filter(vsi, macaddr, vlan);
if (!f) {
f = kzalloc(sizeof(*f), GFP_ATOMIC);
if (!f)
return NULL;
/* Update the boolean indicating if we need to function in
* VLAN mode.
*/
if (vlan >= 0)
vsi->has_vlan_filter = true;
ether_addr_copy(f->macaddr, macaddr);
f->vlan = vlan;
f->state = I40E_FILTER_NEW;
INIT_HLIST_NODE(&f->hlist);
key = i40e_addr_to_hkey(macaddr);
hash_add(vsi->mac_filter_hash, &f->hlist, key);
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state);
}
/* If we're asked to add a filter that has been marked for removal, it
* is safe to simply restore it to active state. __i40e_del_filter
* will have simply deleted any filters which were previously marked
* NEW or FAILED, so if it is currently marked REMOVE it must have
* previously been ACTIVE. Since we haven't yet run the sync filters
* task, just restore this filter to the ACTIVE state so that the
* sync task leaves it in place
*/
if (f->state == I40E_FILTER_REMOVE)
f->state = I40E_FILTER_ACTIVE;
return f;
}
/**
* __i40e_del_filter - Remove a specific filter from the VSI
* @vsi: VSI to remove from
* @f: the filter to remove from the list
*
* This function should be called instead of i40e_del_filter only if you know
* the exact filter you will remove already, such as via i40e_find_filter or
* i40e_find_mac.
*
* NOTE: This function is expected to be called with mac_filter_hash_lock
* being held.
* ANOTHER NOTE: This function MUST be called from within the context of
* the "safe" variants of any list iterators, e.g. list_for_each_entry_safe()
* instead of list_for_each_entry().
**/
void __i40e_del_filter(struct i40e_vsi *vsi, struct i40e_mac_filter *f)
{
if (!f)
return;
/* If the filter was never added to firmware then we can just delete it
* directly and we don't want to set the status to remove or else an
* admin queue command will unnecessarily fire.
*/
if ((f->state == I40E_FILTER_FAILED) ||
(f->state == I40E_FILTER_NEW)) {
hash_del(&f->hlist);
kfree(f);
} else {
f->state = I40E_FILTER_REMOVE;
}
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state);
}
/**
* i40e_del_filter - Remove a MAC/VLAN filter from the VSI
* @vsi: the VSI to be searched
* @macaddr: the MAC address
* @vlan: the VLAN
*
* NOTE: This function is expected to be called with mac_filter_hash_lock
* being held.
* ANOTHER NOTE: This function MUST be called from within the context of
* the "safe" variants of any list iterators, e.g. list_for_each_entry_safe()
* instead of list_for_each_entry().
**/
void i40e_del_filter(struct i40e_vsi *vsi, const u8 *macaddr, s16 vlan)
{
struct i40e_mac_filter *f;
if (!vsi || !macaddr)
return;
f = i40e_find_filter(vsi, macaddr, vlan);
__i40e_del_filter(vsi, f);
}
/**
* i40e_add_mac_filter - Add a MAC filter for all active VLANs
* @vsi: the VSI to be searched
* @macaddr: the mac address to be filtered
*
* If we're not in VLAN mode, just add the filter to I40E_VLAN_ANY. Otherwise,
* go through all the macvlan filters and add a macvlan filter for each
* unique vlan that already exists. If a PVID has been assigned, instead only
* add the macaddr to that VLAN.
*
* Returns last filter added on success, else NULL
**/
struct i40e_mac_filter *i40e_add_mac_filter(struct i40e_vsi *vsi,
const u8 *macaddr)
{
struct i40e_mac_filter *f, *add = NULL;
struct hlist_node *h;
int bkt;
if (vsi->info.pvid)
return i40e_add_filter(vsi, macaddr,
le16_to_cpu(vsi->info.pvid));
if (!i40e_is_vsi_in_vlan(vsi))
return i40e_add_filter(vsi, macaddr, I40E_VLAN_ANY);
hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
if (f->state == I40E_FILTER_REMOVE)
continue;
add = i40e_add_filter(vsi, macaddr, f->vlan);
if (!add)
return NULL;
}
return add;
}
/**
* i40e_del_mac_filter - Remove a MAC filter from all VLANs
* @vsi: the VSI to be searched
* @macaddr: the mac address to be removed
*
* Removes a given MAC address from a VSI regardless of what VLAN it has been
* associated with.
*
* Returns 0 for success, or error
**/
int i40e_del_mac_filter(struct i40e_vsi *vsi, const u8 *macaddr)
{
struct i40e_mac_filter *f;
struct hlist_node *h;
bool found = false;
int bkt;
lockdep_assert_held(&vsi->mac_filter_hash_lock);
hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
if (ether_addr_equal(macaddr, f->macaddr)) {
__i40e_del_filter(vsi, f);
found = true;
}
}
if (found)
return 0;
else
return -ENOENT;
}
/**
* i40e_set_mac - NDO callback to set mac address
* @netdev: network interface device structure
* @p: pointer to an address structure
*
* Returns 0 on success, negative on failure
**/
static int i40e_set_mac(struct net_device *netdev, void *p)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
struct i40e_hw *hw = &pf->hw;
struct sockaddr *addr = p;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
if (ether_addr_equal(netdev->dev_addr, addr->sa_data)) {
netdev_info(netdev, "already using mac address %pM\n",
addr->sa_data);
return 0;
}
if (test_bit(__I40E_DOWN, pf->state) ||
test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state))
return -EADDRNOTAVAIL;
if (ether_addr_equal(hw->mac.addr, addr->sa_data))
netdev_info(netdev, "returning to hw mac address %pM\n",
hw->mac.addr);
else
netdev_info(netdev, "set new mac address %pM\n", addr->sa_data);
/* Copy the address first, so that we avoid a possible race with
* .set_rx_mode().
* - Remove old address from MAC filter
* - Copy new address
* - Add new address to MAC filter
*/
spin_lock_bh(&vsi->mac_filter_hash_lock);
i40e_del_mac_filter(vsi, netdev->dev_addr);
ether_addr_copy(netdev->dev_addr, addr->sa_data);
i40e_add_mac_filter(vsi, netdev->dev_addr);
spin_unlock_bh(&vsi->mac_filter_hash_lock);
if (vsi->type == I40E_VSI_MAIN) {
i40e_status ret;
ret = i40e_aq_mac_address_write(hw, I40E_AQC_WRITE_TYPE_LAA_WOL,
addr->sa_data, NULL);
if (ret)
netdev_info(netdev, "Ignoring error from firmware on LAA update, status %s, AQ ret %s\n",
i40e_stat_str(hw, ret),
i40e_aq_str(hw, hw->aq.asq_last_status));
}
/* schedule our worker thread which will take care of
* applying the new filter changes
*/
i40e_service_event_schedule(pf);
return 0;
}
/**
* i40e_config_rss_aq - Prepare for RSS using AQ commands
* @vsi: vsi structure
* @seed: RSS hash seed
**/
static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
u8 *lut, u16 lut_size)
{
struct i40e_pf *pf = vsi->back;
struct i40e_hw *hw = &pf->hw;
int ret = 0;
if (seed) {
struct i40e_aqc_get_set_rss_key_data *seed_dw =
(struct i40e_aqc_get_set_rss_key_data *)seed;
ret = i40e_aq_set_rss_key(hw, vsi->id, seed_dw);
if (ret) {
dev_info(&pf->pdev->dev,
"Cannot set RSS key, err %s aq_err %s\n",
i40e_stat_str(hw, ret),
i40e_aq_str(hw, hw->aq.asq_last_status));
return ret;
}
}
if (lut) {
bool pf_lut = vsi->type == I40E_VSI_MAIN ? true : false;
ret = i40e_aq_set_rss_lut(hw, vsi->id, pf_lut, lut, lut_size);
if (ret) {
dev_info(&pf->pdev->dev,
"Cannot set RSS lut, err %s aq_err %s\n",
i40e_stat_str(hw, ret),
i40e_aq_str(hw, hw->aq.asq_last_status));
return ret;
}
}
return ret;
}
/**
* i40e_vsi_config_rss - Prepare for VSI(VMDq) RSS if used
* @vsi: VSI structure
**/
static int i40e_vsi_config_rss(struct i40e_vsi *vsi)
{
struct i40e_pf *pf = vsi->back;
u8 seed[I40E_HKEY_ARRAY_SIZE];
u8 *lut;
int ret;
if (!(pf->hw_features & I40E_HW_RSS_AQ_CAPABLE))
return 0;
if (!vsi->rss_size)
vsi->rss_size = min_t(int, pf->alloc_rss_size,
vsi->num_queue_pairs);
if (!vsi->rss_size)
return -EINVAL;
lut = kzalloc(vsi->rss_table_size, GFP_KERNEL);
if (!lut)
return -ENOMEM;
/* Use the user configured hash keys and lookup table if there is one,
* otherwise use default
*/
if (vsi->rss_lut_user)
memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size);
else
i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size);
if (vsi->rss_hkey_user)
memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE);
else
netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size);
kfree(lut);
return ret;
}
/**
* i40e_vsi_setup_queue_map_mqprio - Prepares mqprio based tc_config
* @vsi: the VSI being configured,
* @ctxt: VSI context structure
* @enabled_tc: number of traffic classes to enable
*
* Prepares VSI tc_config to have queue configurations based on MQPRIO options.
**/
static int i40e_vsi_setup_queue_map_mqprio(struct i40e_vsi *vsi,
struct i40e_vsi_context *ctxt,
u8 enabled_tc)
{
u16 qcount = 0, max_qcount, qmap, sections = 0;
int i, override_q, pow, num_qps, ret;
u8 netdev_tc = 0, offset = 0;
if (vsi->type != I40E_VSI_MAIN)
return -EINVAL;
sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID;
sections |= I40E_AQ_VSI_PROP_SCHED_VALID;
vsi->tc_config.numtc = vsi->mqprio_qopt.qopt.num_tc;
vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1;
num_qps = vsi->mqprio_qopt.qopt.count[0];
/* find the next higher power-of-2 of num queue pairs */
pow = ilog2(num_qps);
if (!is_power_of_2(num_qps))
pow++;
qmap = (offset << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) |
(pow << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT);
/* Setup queue offset/count for all TCs for given VSI */
max_qcount = vsi->mqprio_qopt.qopt.count[0];
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
/* See if the given TC is enabled for the given VSI */
if (vsi->tc_config.enabled_tc & BIT(i)) {
offset = vsi->mqprio_qopt.qopt.offset[i];
qcount = vsi->mqprio_qopt.qopt.count[i];
if (qcount > max_qcount)
max_qcount = qcount;
vsi->tc_config.tc_info[i].qoffset = offset;
vsi->tc_config.tc_info[i].qcount = qcount;
vsi->tc_config.tc_info[i].netdev_tc = netdev_tc++;
} else {
/* TC is not enabled so set the offset to
* default queue and allocate one queue
* for the given TC.
*/
vsi->tc_config.tc_info[i].qoffset = 0;
vsi->tc_config.tc_info[i].qcount = 1;
vsi->tc_config.tc_info[i].netdev_tc = 0;
}
}
/* Set actual Tx/Rx queue pairs */
vsi->num_queue_pairs = offset + qcount;
/* Setup queue TC[0].qmap for given VSI context */
ctxt->info.tc_mapping[0] = cpu_to_le16(qmap);
ctxt->info.mapping_flags |= cpu_to_le16(I40E_AQ_VSI_QUE_MAP_CONTIG);
ctxt->info.queue_mapping[0] = cpu_to_le16(vsi->base_queue);
ctxt->info.valid_sections |= cpu_to_le16(sections);
/* Reconfigure RSS for main VSI with max queue count */
vsi->rss_size = max_qcount;
ret = i40e_vsi_config_rss(vsi);
if (ret) {
dev_info(&vsi->back->pdev->dev,
"Failed to reconfig rss for num_queues (%u)\n",
max_qcount);
return ret;
}
vsi->reconfig_rss = true;
dev_dbg(&vsi->back->pdev->dev,
"Reconfigured rss with num_queues (%u)\n", max_qcount);
/* Find queue count available for channel VSIs and starting offset
* for channel VSIs
*/
override_q = vsi->mqprio_qopt.qopt.count[0];
if (override_q && override_q < vsi->num_queue_pairs) {
vsi->cnt_q_avail = vsi->num_queue_pairs - override_q;
vsi->next_base_queue = override_q;
}
return 0;
}
/**
* i40e_vsi_setup_queue_map - Setup a VSI queue map based on enabled_tc
* @vsi: the VSI being setup
* @ctxt: VSI context structure
* @enabled_tc: Enabled TCs bitmap
* @is_add: True if called before Add VSI
*
* Setup VSI queue mapping for enabled traffic classes.
**/
static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
struct i40e_vsi_context *ctxt,
u8 enabled_tc,
bool is_add)
{
struct i40e_pf *pf = vsi->back;
u16 sections = 0;
u8 netdev_tc = 0;
u16 numtc = 1;
u16 qcount;
u8 offset;
u16 qmap;
int i;
u16 num_tc_qps = 0;
sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID;
offset = 0;
/* Number of queues per enabled TC */
num_tc_qps = vsi->alloc_queue_pairs;
if (enabled_tc && (vsi->back->flags & I40E_FLAG_DCB_ENABLED)) {
/* Find numtc from enabled TC bitmap */
for (i = 0, numtc = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
if (enabled_tc & BIT(i)) /* TC is enabled */
numtc++;
}
if (!numtc) {
dev_warn(&pf->pdev->dev, "DCB is enabled but no TC enabled, forcing TC0\n");
numtc = 1;
}
num_tc_qps = num_tc_qps / numtc;
num_tc_qps = min_t(int, num_tc_qps,
i40e_pf_get_max_q_per_tc(pf));
}
vsi->tc_config.numtc = numtc;
vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1;
/* Do not allow use more TC queue pairs than MSI-X vectors exist */
if (pf->flags & I40E_FLAG_MSIX_ENABLED)
num_tc_qps = min_t(int, num_tc_qps, pf->num_lan_msix);
/* Setup queue offset/count for all TCs for given VSI */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
/* See if the given TC is enabled for the given VSI */
if (vsi->tc_config.enabled_tc & BIT(i)) {
/* TC is enabled */
int pow, num_qps;
switch (vsi->type) {
case I40E_VSI_MAIN:
if (!(pf->flags & (I40E_FLAG_FD_SB_ENABLED |
I40E_FLAG_FD_ATR_ENABLED)) ||
vsi->tc_config.enabled_tc != 1) {
qcount = min_t(int, pf->alloc_rss_size,
num_tc_qps);
break;
}
/* fall through */
case I40E_VSI_FDIR:
case I40E_VSI_SRIOV:
case I40E_VSI_VMDQ2:
default:
qcount = num_tc_qps;
WARN_ON(i != 0);
break;
}
vsi->tc_config.tc_info[i].qoffset = offset;
vsi->tc_config.tc_info[i].qcount = qcount;
/* find the next higher power-of-2 of num queue pairs */
num_qps = qcount;
pow = 0;
while (num_qps && (BIT_ULL(pow) < qcount)) {
pow++;
num_qps >>= 1;
}
vsi->tc_config.tc_info[i].netdev_tc = netdev_tc++;
qmap =
(offset << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) |
(pow << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT);
offset += qcount;
} else {
/* TC is not enabled so set the offset to
* default queue and allocate one queue
* for the given TC.
*/
vsi->tc_config.tc_info[i].qoffset = 0;
vsi->tc_config.tc_info[i].qcount = 1;
vsi->tc_config.tc_info[i].netdev_tc = 0;
qmap = 0;
}
ctxt->info.tc_mapping[i] = cpu_to_le16(qmap);
}
/* Set actual Tx/Rx queue pairs */
vsi->num_queue_pairs = offset;
if ((vsi->type == I40E_VSI_MAIN) && (numtc == 1)) {
if (vsi->req_queue_pairs > 0)
vsi->num_queue_pairs = vsi->req_queue_pairs;
else if (pf->flags & I40E_FLAG_MSIX_ENABLED)
vsi->num_queue_pairs = pf->num_lan_msix;
}
/* Scheduler section valid can only be set for ADD VSI */
if (is_add) {
sections |= I40E_AQ_VSI_PROP_SCHED_VALID;
ctxt->info.up_enable_bits = enabled_tc;
}
if (vsi->type == I40E_VSI_SRIOV) {
ctxt->info.mapping_flags |=
cpu_to_le16(I40E_AQ_VSI_QUE_MAP_NONCONTIG);
for (i = 0; i < vsi->num_queue_pairs; i++)
ctxt->info.queue_mapping[i] =
cpu_to_le16(vsi->base_queue + i);
} else {
ctxt->info.mapping_flags |=
cpu_to_le16(I40E_AQ_VSI_QUE_MAP_CONTIG);
ctxt->info.queue_mapping[0] = cpu_to_le16(vsi->base_queue);
}
ctxt->info.valid_sections |= cpu_to_le16(sections);
}
/**
* i40e_addr_sync - Callback for dev_(mc|uc)_sync to add address
* @netdev: the netdevice
* @addr: address to add
*
* Called by __dev_(mc|uc)_sync when an address needs to be added. We call
* __dev_(uc|mc)_sync from .set_rx_mode and guarantee to hold the hash lock.
*/
static int i40e_addr_sync(struct net_device *netdev, const u8 *addr)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
if (i40e_add_mac_filter(vsi, addr))
return 0;
else
return -ENOMEM;
}
/**
* i40e_addr_unsync - Callback for dev_(mc|uc)_sync to remove address
* @netdev: the netdevice
* @addr: address to add
*
* Called by __dev_(mc|uc)_sync when an address needs to be removed. We call
* __dev_(uc|mc)_sync from .set_rx_mode and guarantee to hold the hash lock.
*/
static int i40e_addr_unsync(struct net_device *netdev, const u8 *addr)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
/* Under some circumstances, we might receive a request to delete
* our own device address from our uc list. Because we store the
* device address in the VSI's MAC/VLAN filter list, we need to ignore
* such requests and not delete our device address from this list.
*/
if (ether_addr_equal(addr, netdev->dev_addr))
return 0;
i40e_del_mac_filter(vsi, addr);
return 0;
}
/**
* i40e_set_rx_mode - NDO callback to set the netdev filters
* @netdev: network interface device structure
**/
static void i40e_set_rx_mode(struct net_device *netdev)
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
spin_lock_bh(&vsi->mac_filter_hash_lock);
__dev_uc_sync(netdev, i40e_addr_sync, i40e_addr_unsync);
__dev_mc_sync(netdev, i40e_addr_sync, i40e_addr_unsync);
spin_unlock_bh(&vsi->mac_filter_hash_lock);
/* check for other flag changes */
if (vsi->current_netdev_flags != vsi->netdev->flags) {
vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
set_bit(__I40E_MACVLAN_SYNC_PENDING, vsi->back->state);
}
}
/**
* i40e_undo_del_filter_entries - Undo the changes made to MAC filter entries
* @vsi: Pointer to VSI struct
* @from: Pointer to list which contains MAC filter entries - changes to
* those entries needs to be undone.
*
* MAC filter entries from this list were slated for deletion.
**/
static void i40e_undo_del_filter_entries(struct i40e_vsi *vsi,
struct hlist_head *from)
{
struct i40e_mac_filter *f;
struct hlist_node *h;
hlist_for_each_entry_safe(f, h, from, hlist) {
u64 key = i40e_addr_to_hkey(f->macaddr);
/* Move the element back into MAC filter list*/
hlist_del(&f->hlist);
hash_add(vsi->mac_filter_hash, &f->hlist, key);
}
}
/**
* i40e_undo_add_filter_entries - Undo the changes made to MAC filter entries
* @vsi: Pointer to vsi struct
* @from: Pointer to list which contains MAC filter entries - changes to
* those entries needs to be undone.
*
* MAC filter entries from this list were slated for addition.
**/
static void i40e_undo_add_filter_entries(struct i40e_vsi *vsi,
struct hlist_head *from)
{
struct i40e_new_mac_filter *new;
struct hlist_node *h;
hlist_for_each_entry_safe(new, h, from, hlist) {
/* We can simply free the wrapper structure */
hlist_del(&new->hlist);
kfree(new);
}
}
/**
* i40e_next_entry - Get the next non-broadcast filter from a list
* @next: pointer to filter in list
*
* Returns the next non-broadcast filter in the list. Required so that we
* ignore broadcast filters within the list, since these are not handled via
* the normal firmware update path.
*/
static
struct i40e_new_mac_filter *i40e_next_filter(struct i40e_new_mac_filter *next)
{
hlist_for_each_entry_continue(next, hlist) {
if (!is_broadcast_ether_addr(next->f->macaddr))
return next;
}
return NULL;
}
/**
* i40e_update_filter_state - Update filter state based on return data
* from firmware
* @count: Number of filters added
* @add_list: return data from fw
* @add_head: pointer to first filter in current batch
*
* MAC filter entries from list were slated to be added to device. Returns
* number of successful filters. Note that 0 does NOT mean success!
**/
static int
i40e_update_filter_state(int count,
struct i40e_aqc_add_macvlan_element_data *add_list,
struct i40e_new_mac_filter *add_head)
{
int retval = 0;
int i;
for (i = 0; i < count; i++) {
/* Always check status of each filter. We don't need to check
* the firmware return status because we pre-set the filter
* status to I40E_AQC_MM_ERR_NO_RES when sending the filter
* request to the adminq. Thus, if it no longer matches then
* we know the filter is active.
*/
if (add_list[i].match_method == I40E_AQC_MM_ERR_NO_RES) {
add_head->state = I40E_FILTER_FAILED;
} else {
add_head->state = I40E_FILTER_ACTIVE;
retval++;
}
add_head = i40e_next_filter(add_head);
if (!add_head)
break;
}
return retval;
}
/**
* i40e_aqc_del_filters - Request firmware to delete a set of filters
* @vsi: ptr to the VSI
* @vsi_name: name to display in messages
* @list: the list of filters to send to firmware
* @num_del: the number of filters to delete
* @retval: Set to -EIO on failure to delete
*
* Send a request to firmware via AdminQ to delete a set of filters. Uses
* *retval instead of a return value so that success does not force ret_val to
* be set to 0. This ensures that a sequence of calls to this function
* preserve the previous value of *retval on successful delete.
*/
static
void i40e_aqc_del_filters(struct i40e_vsi *vsi, const char *vsi_name,
struct i40e_aqc_remove_macvlan_element_data *list,
int num_del, int *retval)
{
struct i40e_hw *hw = &vsi->back->hw;
i40e_status aq_ret;
int aq_err;
aq_ret = i40e_aq_remove_macvlan(hw, vsi->seid, list, num_del, NULL);
aq_err = hw->aq.asq_last_status;
/* Explicitly ignore and do not report when firmware returns ENOENT */
if (aq_ret && !(aq_err == I40E_AQ_RC_ENOENT)) {
*retval = -EIO;
dev_info(&vsi->back->pdev->dev,
"ignoring delete macvlan error on %s, err %s, aq_err %s\n",
vsi_name, i40e_stat_str(hw, aq_ret),
i40e_aq_str(hw, aq_err));
}
}
/**
* i40e_aqc_add_filters - Request firmware to add a set of filters
* @vsi: ptr to the VSI
* @vsi_name: name to display in messages
* @list: the list of filters to send to firmware
* @add_head: Position in the add hlist
* @num_add: the number of filters to add
*
* Send a request to firmware via AdminQ to add a chunk of filters. Will set
* __I40E_VSI_OVERFLOW_PROMISC bit in vsi->state if the firmware has run out of
* space for more filters.
*/
static
void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name,
struct i40e_aqc_add_macvlan_element_data *list,
struct i40e_new_mac_filter *add_head,
int num_add)
{
struct i40e_hw *hw = &vsi->back->hw;
int aq_err, fcnt;
i40e_aq_add_macvlan(hw, vsi->seid, list, num_add, NULL);
aq_err = hw->aq.asq_last_status;
fcnt = i40e_update_filter_state(num_add, list, add_head);
if (fcnt != num_add) {
if (vsi->type == I40E_VSI_MAIN) {
set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
dev_warn(&vsi->back->pdev->dev,
"Error %s adding RX filters on %s, promiscuous mode forced on\n",
i40e_aq_str(hw, aq_err), vsi_name);
} else if (vsi->type == I40E_VSI_SRIOV ||
vsi->type == I40E_VSI_VMDQ1 ||
vsi->type == I40E_VSI_VMDQ2) {
dev_warn(&vsi->back->pdev->dev,
"Error %s adding RX filters on %s, please set promiscuous on manually for %s\n",
i40e_aq_str(hw, aq_err), vsi_name, vsi_name);
} else {
dev_warn(&vsi->back->pdev->dev,
"Error %s adding RX filters on %s, incorrect VSI type: %i.\n",
i40e_aq_str(hw, aq_err), vsi_name, vsi->type);
}
}
}
/**
* i40e_aqc_broadcast_filter - Set promiscuous broadcast flags
* @vsi: pointer to the VSI
* @vsi_name: the VSI name
* @f: filter data
*
* This function sets or clears the promiscuous broadcast flags for VLAN
* filters in order to properly receive broadcast frames. Assumes that only
* broadcast filters are passed.
*
* Returns status indicating success or failure;
**/
static i40e_status
i40e_aqc_broadcast_filter(struct i40e_vsi *vsi, const char *vsi_name,
struct i40e_mac_filter *f)
{
bool enable = f->state == I40E_FILTER_NEW;
struct i40e_hw *hw = &vsi->back->hw;
i40e_status aq_ret;
if (f->vlan == I40E_VLAN_ANY) {
aq_ret = i40e_aq_set_vsi_broadcast(hw,
vsi->seid,
enable,
NULL);
} else {
aq_ret = i40e_aq_set_vsi_bc_promisc_on_vlan(hw,
vsi->seid,
enable,
f->vlan,
NULL);
}
if (aq_ret) {
set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
dev_warn(&vsi->back->pdev->dev,
"Error %s, forcing overflow promiscuous on %s\n",
i40e_aq_str(hw, hw->aq.asq_last_status),
vsi_name);
}
return aq_ret;
}
/**
* i40e_set_promiscuous - set promiscuous mode
* @pf: board private structure
* @promisc: promisc on or off
*
* There are different ways of setting promiscuous mode on a PF depending on
* what state/environment we're in. This identifies and sets it appropriately.
* Returns 0 on success.
**/
static int i40e_set_promiscuous(struct i40e_pf *pf, bool promisc)
{
struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
struct i40e_hw *hw = &pf->hw;
i40e_status aq_ret;
if (vsi->type == I40E_VSI_MAIN &&
pf->lan_veb != I40E_NO_VEB &&
!(pf->flags & I40E_FLAG_MFP_ENABLED)) {
/* set defport ON for Main VSI instead of true promisc
* this way we will get all unicast/multicast and VLAN
* promisc behavior but will not get VF or VMDq traffic
* replicated on the Main VSI.
*/
if (promisc)
aq_ret = i40e_aq_set_default_vsi(hw,
vsi->seid,
NULL);
else
aq_ret = i40e_aq_clear_default_vsi(hw,
vsi->seid,
NULL);
if (aq_ret) {
dev_info(&pf->pdev->dev,
"Set default VSI failed, err %s, aq_err %s\n",
i40e_stat_str(hw, aq_ret),
i40e_aq_str(hw, hw->aq.asq_last_status));
}
} else {
aq_ret = i40e_aq_set_vsi_unicast_promiscuous(
hw,
vsi->seid,
promisc, NULL,
true);
if (aq_ret) {
dev_info(&pf->pdev->dev,
"set unicast promisc failed, err %s, aq_err %s\n",
i40e_stat_str(hw, aq_ret),
i40e_aq_str(hw, hw->aq.asq_last_status));
}
aq_ret = i40e_aq_set_vsi_multicast_promiscuous(
hw,
vsi->seid,
promisc, NULL);
if (aq_ret) {
dev_info(&pf->pdev->dev,
"set multicast promisc failed, err %s, aq_err %s\n",
i40e_stat_str(hw, aq_ret),
i40e_aq_str(hw, hw->aq.asq_last_status));
}
}
if (!aq_ret)
pf->cur_promisc = promisc;
return aq_ret;
}
/**
* i40e_sync_vsi_filters - Update the VSI filter list to the HW
* @vsi: ptr to the VSI
*
* Push any outstanding VSI filter changes through the AdminQ.
*
* Returns 0 or error value
**/
int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
{
struct hlist_head tmp_add_list, tmp_del_list;
struct i40e_mac_filter *f;
struct i40e_new_mac_filter *new, *add_head = NULL;
struct i40e_hw *hw = &vsi->back->hw;
bool old_overflow, new_overflow;
unsigned int failed_filters = 0;
unsigned int vlan_filters = 0;
char vsi_name[16] = "PF";
int filter_list_len = 0;
i40e_status aq_ret = 0;
u32 changed_flags = 0;
struct hlist_node *h;
struct i40e_pf *pf;
int num_add = 0;
int num_del = 0;
int retval = 0;
u16 cmd_flags;
int list_size;
int bkt;
/* empty array typed pointers, kcalloc later */
struct i40e_aqc_add_macvlan_element_data *add_list;
struct i40e_aqc_remove_macvlan_element_data *del_list;
while (test_and_set_bit(__I40E_VSI_SYNCING_FILTERS, vsi->state))
usleep_range(1000, 2000);
pf = vsi->back;
old_overflow = test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
if (vsi->netdev) {
changed_flags = vsi->current_netdev_flags ^ vsi->netdev->flags;
vsi->current_netdev_flags = vsi->netdev->flags;
}
INIT_HLIST_HEAD(&tmp_add_list);
INIT_HLIST_HEAD(&tmp_del_list);
if (vsi->type == I40E_VSI_SRIOV)
snprintf(vsi_name, sizeof(vsi_name) - 1, "VF %d", vsi->vf_id);
else if (vsi->type != I40E_VSI_MAIN)
snprintf(vsi_name, sizeof(vsi_name) - 1, "vsi %d", vsi->seid);
if (vsi->flags & I40E_VSI_FLAG_FILTER_CHANGED) {
vsi->flags &= ~I40E_VSI_FLAG_FILTER_CHANGED;
spin_lock_bh(&vsi->mac_filter_hash_lock);
/* Create a list of filters to delete. */
hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) {
if (f->state == I40E_FILTER_REMOVE) {
/* Move the element into temporary del_list */
hash_del(&f->hlist);
hlist_add_head(&f->hlist, &tmp_del_list);
/* Avoid counting removed filters */
continue;
}
if (f->state == I40E_FILTER_NEW) {
/* Create a temporary i40e_new_mac_filter */
new = kzalloc(sizeof(*new), GFP_ATOMIC);
if (!new)
goto err_no_memory_locked;
/* Store pointer to the real filter */
new->f = f;
new->state = f->state;
/* Add it to the hash list */
hlist_add_head(&new->hlist, &tmp_add_list);
}
/* Count the number of active (current and new) VLAN
* filters we have now. Does not count filters which
* are marked for deletion.
*/
if (f->vlan > 0)
vlan_filters++;
}
retval = i40e_correct_mac_vlan_filters(vsi,
&tmp_add_list,
&tmp_del_list,
vlan_filters);
if (retval)
goto err_no_memory_locked;
spin_unlock_bh(&vsi->mac_filter_hash_lock);
}
/* Now process 'del_list' outside the lock */
if (!hlist_empty(&tmp_del_list)) {
filter_list_len = hw->aq.asq_buf_size /
sizeof(struct i40e_aqc_remove_macvlan_element_data);
list_size = filter_list_len *
sizeof(struct i40e_aqc_remove_macvlan_element_data);
del_list = kzalloc(list_size, GFP_ATOMIC);
if (!del_list)
goto err_no_memory;
hlist_for_each_entry_safe(f, h, &tmp_del_list, hlist) {
cmd_flags = 0;
/* handle broadcast filters by updating the broadcast
* promiscuous flag and release filter list.
*/
if (is_broadcast_ether_addr(f->macaddr)) {
i40e_aqc_broadcast_filter(vsi, vsi_name, f);
hlist_del(&f->hlist);
kfree(f);
continue;
}
/* add to delete list */
ether_addr_copy(del_list[num_del].mac_addr, f->macaddr);
if (f->vlan == I40E_VLAN_ANY) {
del_list[num_del].vlan_tag = 0;
cmd_flags |= I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
} else {
del_list[num_del].vlan_tag =
cpu_to_le16((u16)(f->vlan));
}
cmd_flags |= I40E_AQC_MACVLAN_DEL_PERFECT_MATCH;
del_list[num_del].flags = cmd_flags;
num_del++;
/* flush a full buffer */
if (num_del == filter_list_len) {
i40e_aqc_del_filters(vsi, vsi_name, del_list,
num_del, &retval);
memset(del_list, 0, list_size);
num_del = 0;
}
/* Release memory for MAC filter entries which were
* synced up with HW.
*/
hlist_del(&f->hlist);
kfree(f);
}
if (num_del) {
i40e_aqc_del_filters(vsi, vsi_name, del_list,
num_del, &retval);
}
kfree(del_list);
del_list = NULL;
}
if (!hlist_empty(&tmp_add_list)) {
/* Do all the adds now. */
filter_list_len = hw->aq.asq_buf_size /
sizeof(struct i40e_aqc_add_macvlan_element_data);
list_size = filter_list_len *
sizeof(struct i40e_aqc_add_macvlan_element_data);
add_list = kzalloc(list_size, GFP_ATOMIC);
if (!add_list)
goto err_no_memory;
num_add = 0;
hlist_for_each_entry_safe(new, h, &tmp_add_list, hlist) {
/* handle broadcast filters by updating the broadcast
* promiscuous flag instead of adding a MAC filter.
*/
if (is_broadcast_ether_addr(new->f->macaddr)) {
if (i40e_aqc_broadcast_filter(vsi, vsi_name,
new->f))
new->state = I40E_FILTER_FAILED;
else
new->state = I40E_FILTER_ACTIVE;
continue;
}
/* add to add array */
if (num_add == 0)
add_head = new;
cmd_flags = 0;
ether_addr_copy(add_list[num_add].mac_addr,
new->f->macaddr);
if (new->f->vlan == I40E_VLAN_ANY) {
add_list[num_add].vlan_tag = 0;
cmd_flags |= I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
} else {
add_list[num_add].vlan_tag =
cpu_to_le16((u16)(new->f->vlan));
}
add_list[num_add].queue_number = 0;
/* set invalid match method for later detection */
add_list[num_add].match_method = I40E_AQC_MM_ERR_NO_RES;
cmd_flags |= I40E_AQC_MACVLAN_ADD_PERFECT_MATCH;
add_list[num_add].flags = cpu_to_le16(cmd_flags);
num_add++;
/* flush a full buffer */
if (num_add == filter_list_len) {
i40e_aqc_add_filters(vsi, vsi_name, add_list,
add_head, num_add);
memset(add_list, 0, list_size);
num_add = 0;
}
}
if (num_add) {
i40e_aqc_add_filters(vsi, vsi_name, add_list, add_head,
num_add);
}
/* Now move all of the filters from the temp add list back to
* the VSI's list.
*/
spin_lock_bh(&vsi->mac_filter_hash_lock);
hlist_for_each_entry_safe(new, h, &tmp_add_list, hlist) {
/* Only update the state if we're still NEW */
if (new->f->state == I40E_FILTER_NEW)
new->f->state = new->state;
hlist_del(&new->hlist);
kfree(new);
}
spin_unlock_bh(&vsi->mac_filter_hash_lock);
kfree(add_list);
add_list = NULL;
}
/* Determine the number of active and failed filters. */
spin_lock_bh(&vsi->mac_filter_hash_lock);
vsi->active_filters = 0;
hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) {
if (f->state == I40E_FILTER_ACTIVE)
vsi->active_filters++;
else if (f->state == I40E_FILTER_FAILED)
failed_filters++;
}
spin_unlock_bh(&vsi->mac_filter_hash_lock);
/* Check if we are able to exit overflow promiscuous mode. We can
* safely exit if we didn't just enter, we no longer have any failed
* filters, and we have reduced filters below the threshold value.
*/
if (old_overflow && !failed_filters &&
vsi->active_filters < vsi->promisc_threshold) {
dev_info(&pf->pdev->dev,
"filter logjam cleared on %s, leaving overflow promiscuous mode\n",
vsi_name);
clear_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
vsi->promisc_threshold = 0;
}
/* if the VF is not trusted do not do promisc */
if ((vsi->type == I40E_VSI_SRIOV) && !pf->vf[vsi->vf_id].trusted) {
clear_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
goto out;
}
new_overflow = test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state);
/* If we are entering overflow promiscuous, we need to calculate a new
* threshold for when we are safe to exit
*/
if (!old_overflow && new_overflow)
vsi->promisc_threshold = (vsi->active_filters * 3) / 4;
/* check for changes in promiscuous modes */
if (changed_flags & IFF_ALLMULTI) {
bool cur_multipromisc;
cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI);
aq_ret =<