/* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2014-2016 Broadcom Corporation
* Copyright (c) 2016-2017 Broadcom Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*/
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/stringify.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/linkmode.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>
#include <linux/firmware.h>
#include <linux/utsname.h>
#include <linux/time.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/net_tstamp.h>
#include <linux/timecounter.h>
#include <net/netdev_queues.h>
#include <net/netlink.h>
#include <linux/bnxt/hsi.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
#include "bnxt_ulp.h"
#include "bnxt_xdp.h"
#include "bnxt_ptp.h"
#include "bnxt_ethtool.h"
#include "bnxt_nvm_defs.h" /* NVRAM content constant and structure defs */
#include "bnxt_fw_hdr.h" /* Firmware hdr constant and structure defs */
#include "bnxt_coredump.h"
#define BNXT_NVM_ERR_MSG(dev, extack, msg) \
do { \
if (extack) \
NL_SET_ERR_MSG_MOD(extack, msg); \
netdev_err(dev, "%s\n" , msg); \
} while (0)
static u32 bnxt_get_msglevel(struct net_device *dev)
{
struct bnxt *bp = netdev_priv(dev);
return bp->msg_enable;
}
static void bnxt_set_msglevel(struct net_device *dev, u32 value)
{
struct bnxt *bp = netdev_priv(dev);
bp->msg_enable = value;
}
static int bnxt_get_coalesce(struct net_device *dev,
struct ethtool_coalesce *coal,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
struct bnxt_coal *hw_coal;
u16 mult;
memset(coal, 0, sizeof (*coal));
coal->use_adaptive_rx_coalesce = bp->flags & BNXT_FLAG_DIM;
hw_coal = &bp->rx_coal;
mult = hw_coal->bufs_per_record;
coal->rx_coalesce_usecs = hw_coal->coal_ticks;
coal->rx_max_coalesced_frames = hw_coal->coal_bufs / mult;
coal->rx_coalesce_usecs_irq = hw_coal->coal_ticks_irq;
coal->rx_max_coalesced_frames_irq = hw_coal->coal_bufs_irq / mult;
if (hw_coal->flags &
RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_TIMER_RESET)
kernel_coal->use_cqe_mode_rx = true ;
hw_coal = &bp->tx_coal;
mult = hw_coal->bufs_per_record;
coal->tx_coalesce_usecs = hw_coal->coal_ticks;
coal->tx_max_coalesced_frames = hw_coal->coal_bufs / mult;
coal->tx_coalesce_usecs_irq = hw_coal->coal_ticks_irq;
coal->tx_max_coalesced_frames_irq = hw_coal->coal_bufs_irq / mult;
if (hw_coal->flags &
RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_TIMER_RESET)
kernel_coal->use_cqe_mode_tx = true ;
coal->stats_block_coalesce_usecs = bp->stats_coal_ticks;
return 0;
}
static int bnxt_set_coalesce(struct net_device *dev,
struct ethtool_coalesce *coal,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
bool update_stats = false ;
struct bnxt_coal *hw_coal;
int rc = 0;
u16 mult;
if (coal->use_adaptive_rx_coalesce) {
bp->flags |= BNXT_FLAG_DIM;
} else {
if (bp->flags & BNXT_FLAG_DIM) {
bp->flags &= ~(BNXT_FLAG_DIM);
goto reset_coalesce;
}
}
if ((kernel_coal->use_cqe_mode_rx || kernel_coal->use_cqe_mode_tx) &&
!(bp->coal_cap.cmpl_params &
RING_AGGINT_QCAPS_RESP_CMPL_PARAMS_TIMER_RESET))
return -EOPNOTSUPP;
hw_coal = &bp->rx_coal;
mult = hw_coal->bufs_per_record;
hw_coal->coal_ticks = coal->rx_coalesce_usecs;
hw_coal->coal_bufs = coal->rx_max_coalesced_frames * mult;
hw_coal->coal_ticks_irq = coal->rx_coalesce_usecs_irq;
hw_coal->coal_bufs_irq = coal->rx_max_coalesced_frames_irq * mult;
hw_coal->flags &=
~RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_TIMER_RESET;
if (kernel_coal->use_cqe_mode_rx)
hw_coal->flags |=
RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_TIMER_RESET;
hw_coal = &bp->tx_coal;
mult = hw_coal->bufs_per_record;
hw_coal->coal_ticks = coal->tx_coalesce_usecs;
hw_coal->coal_bufs = coal->tx_max_coalesced_frames * mult;
hw_coal->coal_ticks_irq = coal->tx_coalesce_usecs_irq;
hw_coal->coal_bufs_irq = coal->tx_max_coalesced_frames_irq * mult;
hw_coal->flags &=
~RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_TIMER_RESET;
if (kernel_coal->use_cqe_mode_tx)
hw_coal->flags |=
RING_CMPL_RING_CFG_AGGINT_PARAMS_REQ_FLAGS_TIMER_RESET;
if (bp->stats_coal_ticks != coal->stats_block_coalesce_usecs) {
u32 stats_ticks = coal->stats_block_coalesce_usecs;
/* Allow 0, which means disable. */
if (stats_ticks)
stats_ticks = clamp_t(u32, stats_ticks,
BNXT_MIN_STATS_COAL_TICKS,
BNXT_MAX_STATS_COAL_TICKS);
stats_ticks = rounddown(stats_ticks, BNXT_MIN_STATS_COAL_TICKS);
bp->stats_coal_ticks = stats_ticks;
if (bp->stats_coal_ticks)
bp->current_interval =
bp->stats_coal_ticks * HZ / 1000000;
else
bp->current_interval = BNXT_TIMER_INTERVAL;
update_stats = true ;
}
reset_coalesce:
if (test_bit(BNXT_STATE_OPEN, &bp->state)) {
if (update_stats) {
bnxt_close_nic(bp, true , false );
rc = bnxt_open_nic(bp, true , false );
} else {
rc = bnxt_hwrm_set_coal(bp);
}
}
return rc;
}
static const char * const bnxt_ring_rx_stats_str[] = {
"rx_ucast_packets" ,
"rx_mcast_packets" ,
"rx_bcast_packets" ,
"rx_discards" ,
"rx_errors" ,
"rx_ucast_bytes" ,
"rx_mcast_bytes" ,
"rx_bcast_bytes" ,
};
static const char * const bnxt_ring_tx_stats_str[] = {
"tx_ucast_packets" ,
"tx_mcast_packets" ,
"tx_bcast_packets" ,
"tx_errors" ,
"tx_discards" ,
"tx_ucast_bytes" ,
"tx_mcast_bytes" ,
"tx_bcast_bytes" ,
};
static const char * const bnxt_ring_tpa_stats_str[] = {
"tpa_packets" ,
"tpa_bytes" ,
"tpa_events" ,
"tpa_aborts" ,
};
static const char * const bnxt_ring_tpa2_stats_str[] = {
"rx_tpa_eligible_pkt" ,
"rx_tpa_eligible_bytes" ,
"rx_tpa_pkt" ,
"rx_tpa_bytes" ,
"rx_tpa_errors" ,
"rx_tpa_events" ,
};
static const char * const bnxt_rx_sw_stats_str[] = {
"rx_l4_csum_errors" ,
"rx_resets" ,
"rx_buf_errors" ,
};
static const char * const bnxt_cmn_sw_stats_str[] = {
"missed_irqs" ,
};
#define BNXT_RX_STATS_ENTRY(counter) \
{ BNXT_RX_STATS_OFFSET(counter), __stringify(counter) }
#define BNXT_TX_STATS_ENTRY(counter) \
{ BNXT_TX_STATS_OFFSET(counter), __stringify(counter) }
#define BNXT_RX_STATS_EXT_ENTRY(counter) \
{ BNXT_RX_STATS_EXT_OFFSET(counter), __stringify(counter) }
#define BNXT_TX_STATS_EXT_ENTRY(counter) \
{ BNXT_TX_STATS_EXT_OFFSET(counter), __stringify(counter) }
#define BNXT_RX_STATS_EXT_PFC_ENTRY(n) \
BNXT_RX_STATS_EXT_ENTRY(pfc_pri## n## _rx_duration_us), \
BNXT_RX_STATS_EXT_ENTRY(pfc_pri## n## _rx_transitions)
#define BNXT_TX_STATS_EXT_PFC_ENTRY(n) \
BNXT_TX_STATS_EXT_ENTRY(pfc_pri## n## _tx_duration_us), \
BNXT_TX_STATS_EXT_ENTRY(pfc_pri## n## _tx_transitions)
#define BNXT_RX_STATS_EXT_PFC_ENTRIES \
BNXT_RX_STATS_EXT_PFC_ENTRY(0), \
BNXT_RX_STATS_EXT_PFC_ENTRY(1), \
BNXT_RX_STATS_EXT_PFC_ENTRY(2), \
BNXT_RX_STATS_EXT_PFC_ENTRY(3), \
BNXT_RX_STATS_EXT_PFC_ENTRY(4), \
BNXT_RX_STATS_EXT_PFC_ENTRY(5), \
BNXT_RX_STATS_EXT_PFC_ENTRY(6), \
BNXT_RX_STATS_EXT_PFC_ENTRY(7)
#define BNXT_TX_STATS_EXT_PFC_ENTRIES \
BNXT_TX_STATS_EXT_PFC_ENTRY(0), \
BNXT_TX_STATS_EXT_PFC_ENTRY(1), \
BNXT_TX_STATS_EXT_PFC_ENTRY(2), \
BNXT_TX_STATS_EXT_PFC_ENTRY(3), \
BNXT_TX_STATS_EXT_PFC_ENTRY(4), \
BNXT_TX_STATS_EXT_PFC_ENTRY(5), \
BNXT_TX_STATS_EXT_PFC_ENTRY(6), \
BNXT_TX_STATS_EXT_PFC_ENTRY(7)
#define BNXT_RX_STATS_EXT_COS_ENTRY(n) \
BNXT_RX_STATS_EXT_ENTRY(rx_bytes_cos## n), \
BNXT_RX_STATS_EXT_ENTRY(rx_packets_cos## n)
#define BNXT_TX_STATS_EXT_COS_ENTRY(n) \
BNXT_TX_STATS_EXT_ENTRY(tx_bytes_cos## n), \
BNXT_TX_STATS_EXT_ENTRY(tx_packets_cos## n)
#define BNXT_RX_STATS_EXT_COS_ENTRIES \
BNXT_RX_STATS_EXT_COS_ENTRY(0), \
BNXT_RX_STATS_EXT_COS_ENTRY(1), \
BNXT_RX_STATS_EXT_COS_ENTRY(2), \
BNXT_RX_STATS_EXT_COS_ENTRY(3), \
BNXT_RX_STATS_EXT_COS_ENTRY(4), \
BNXT_RX_STATS_EXT_COS_ENTRY(5), \
BNXT_RX_STATS_EXT_COS_ENTRY(6), \
BNXT_RX_STATS_EXT_COS_ENTRY(7) \
#define BNXT_TX_STATS_EXT_COS_ENTRIES \
BNXT_TX_STATS_EXT_COS_ENTRY(0), \
BNXT_TX_STATS_EXT_COS_ENTRY(1), \
BNXT_TX_STATS_EXT_COS_ENTRY(2), \
BNXT_TX_STATS_EXT_COS_ENTRY(3), \
BNXT_TX_STATS_EXT_COS_ENTRY(4), \
BNXT_TX_STATS_EXT_COS_ENTRY(5), \
BNXT_TX_STATS_EXT_COS_ENTRY(6), \
BNXT_TX_STATS_EXT_COS_ENTRY(7) \
#define BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(n) \
BNXT_RX_STATS_EXT_ENTRY(rx_discard_bytes_cos## n), \
BNXT_RX_STATS_EXT_ENTRY(rx_discard_packets_cos## n)
#define BNXT_RX_STATS_EXT_DISCARD_COS_ENTRIES \
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(0), \
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(1), \
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(2), \
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(3), \
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(4), \
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(5), \
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(6), \
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRY(7)
#define BNXT_RX_STATS_PRI_ENTRY(counter, n) \
{ BNXT_RX_STATS_EXT_OFFSET(counter## _cos0), \
__stringify(counter## _pri## n) }
#define BNXT_TX_STATS_PRI_ENTRY(counter, n) \
{ BNXT_TX_STATS_EXT_OFFSET(counter## _cos0), \
__stringify(counter## _pri## n) }
#define BNXT_RX_STATS_PRI_ENTRIES(counter) \
BNXT_RX_STATS_PRI_ENTRY(counter, 0), \
BNXT_RX_STATS_PRI_ENTRY(counter, 1), \
BNXT_RX_STATS_PRI_ENTRY(counter, 2), \
BNXT_RX_STATS_PRI_ENTRY(counter, 3), \
BNXT_RX_STATS_PRI_ENTRY(counter, 4), \
BNXT_RX_STATS_PRI_ENTRY(counter, 5), \
BNXT_RX_STATS_PRI_ENTRY(counter, 6), \
BNXT_RX_STATS_PRI_ENTRY(counter, 7)
#define BNXT_TX_STATS_PRI_ENTRIES(counter) \
BNXT_TX_STATS_PRI_ENTRY(counter, 0), \
BNXT_TX_STATS_PRI_ENTRY(counter, 1), \
BNXT_TX_STATS_PRI_ENTRY(counter, 2), \
BNXT_TX_STATS_PRI_ENTRY(counter, 3), \
BNXT_TX_STATS_PRI_ENTRY(counter, 4), \
BNXT_TX_STATS_PRI_ENTRY(counter, 5), \
BNXT_TX_STATS_PRI_ENTRY(counter, 6), \
BNXT_TX_STATS_PRI_ENTRY(counter, 7)
enum {
RX_TOTAL_DISCARDS,
TX_TOTAL_DISCARDS,
RX_NETPOLL_DISCARDS,
};
static const char *const bnxt_ring_err_stats_arr[] = {
"rx_total_l4_csum_errors" ,
"rx_total_resets" ,
"rx_total_buf_errors" ,
"rx_total_oom_discards" ,
"rx_total_netpoll_discards" ,
"rx_total_ring_discards" ,
"tx_total_resets" ,
"tx_total_ring_discards" ,
"total_missed_irqs" ,
};
#define NUM_RING_RX_SW_STATS ARRAY_SIZE(bnxt_rx_sw_stats_str)
#define NUM_RING_CMN_SW_STATS ARRAY_SIZE(bnxt_cmn_sw_stats_str)
#define NUM_RING_RX_HW_STATS ARRAY_SIZE(bnxt_ring_rx_stats_str)
#define NUM_RING_TX_HW_STATS ARRAY_SIZE(bnxt_ring_tx_stats_str)
static const struct {
long offset;
char string[ETH_GSTRING_LEN];
} bnxt_port_stats_arr[] = {
BNXT_RX_STATS_ENTRY(rx_64b_frames),
BNXT_RX_STATS_ENTRY(rx_65b_127b_frames),
BNXT_RX_STATS_ENTRY(rx_128b_255b_frames),
BNXT_RX_STATS_ENTRY(rx_256b_511b_frames),
BNXT_RX_STATS_ENTRY(rx_512b_1023b_frames),
BNXT_RX_STATS_ENTRY(rx_1024b_1518b_frames),
BNXT_RX_STATS_ENTRY(rx_good_vlan_frames),
BNXT_RX_STATS_ENTRY(rx_1519b_2047b_frames),
BNXT_RX_STATS_ENTRY(rx_2048b_4095b_frames),
BNXT_RX_STATS_ENTRY(rx_4096b_9216b_frames),
BNXT_RX_STATS_ENTRY(rx_9217b_16383b_frames),
BNXT_RX_STATS_ENTRY(rx_total_frames),
BNXT_RX_STATS_ENTRY(rx_ucast_frames),
BNXT_RX_STATS_ENTRY(rx_mcast_frames),
BNXT_RX_STATS_ENTRY(rx_bcast_frames),
BNXT_RX_STATS_ENTRY(rx_fcs_err_frames),
BNXT_RX_STATS_ENTRY(rx_ctrl_frames),
BNXT_RX_STATS_ENTRY(rx_pause_frames),
BNXT_RX_STATS_ENTRY(rx_pfc_frames),
BNXT_RX_STATS_ENTRY(rx_align_err_frames),
BNXT_RX_STATS_ENTRY(rx_ovrsz_frames),
BNXT_RX_STATS_ENTRY(rx_jbr_frames),
BNXT_RX_STATS_ENTRY(rx_mtu_err_frames),
BNXT_RX_STATS_ENTRY(rx_tagged_frames),
BNXT_RX_STATS_ENTRY(rx_double_tagged_frames),
BNXT_RX_STATS_ENTRY(rx_good_frames),
BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri0),
BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri1),
BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri2),
BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri3),
BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri4),
BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri5),
BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri6),
BNXT_RX_STATS_ENTRY(rx_pfc_ena_frames_pri7),
BNXT_RX_STATS_ENTRY(rx_undrsz_frames),
BNXT_RX_STATS_ENTRY(rx_eee_lpi_events),
BNXT_RX_STATS_ENTRY(rx_eee_lpi_duration),
BNXT_RX_STATS_ENTRY(rx_bytes),
BNXT_RX_STATS_ENTRY(rx_runt_bytes),
BNXT_RX_STATS_ENTRY(rx_runt_frames),
BNXT_RX_STATS_ENTRY(rx_stat_discard),
BNXT_RX_STATS_ENTRY(rx_stat_err),
BNXT_TX_STATS_ENTRY(tx_64b_frames),
BNXT_TX_STATS_ENTRY(tx_65b_127b_frames),
BNXT_TX_STATS_ENTRY(tx_128b_255b_frames),
BNXT_TX_STATS_ENTRY(tx_256b_511b_frames),
BNXT_TX_STATS_ENTRY(tx_512b_1023b_frames),
BNXT_TX_STATS_ENTRY(tx_1024b_1518b_frames),
BNXT_TX_STATS_ENTRY(tx_good_vlan_frames),
BNXT_TX_STATS_ENTRY(tx_1519b_2047b_frames),
BNXT_TX_STATS_ENTRY(tx_2048b_4095b_frames),
BNXT_TX_STATS_ENTRY(tx_4096b_9216b_frames),
BNXT_TX_STATS_ENTRY(tx_9217b_16383b_frames),
BNXT_TX_STATS_ENTRY(tx_good_frames),
BNXT_TX_STATS_ENTRY(tx_total_frames),
BNXT_TX_STATS_ENTRY(tx_ucast_frames),
BNXT_TX_STATS_ENTRY(tx_mcast_frames),
BNXT_TX_STATS_ENTRY(tx_bcast_frames),
BNXT_TX_STATS_ENTRY(tx_pause_frames),
BNXT_TX_STATS_ENTRY(tx_pfc_frames),
BNXT_TX_STATS_ENTRY(tx_jabber_frames),
BNXT_TX_STATS_ENTRY(tx_fcs_err_frames),
BNXT_TX_STATS_ENTRY(tx_err),
BNXT_TX_STATS_ENTRY(tx_fifo_underruns),
BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri0),
BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri1),
BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri2),
BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri3),
BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri4),
BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri5),
BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri6),
BNXT_TX_STATS_ENTRY(tx_pfc_ena_frames_pri7),
BNXT_TX_STATS_ENTRY(tx_eee_lpi_events),
BNXT_TX_STATS_ENTRY(tx_eee_lpi_duration),
BNXT_TX_STATS_ENTRY(tx_total_collisions),
BNXT_TX_STATS_ENTRY(tx_bytes),
BNXT_TX_STATS_ENTRY(tx_xthol_frames),
BNXT_TX_STATS_ENTRY(tx_stat_discard),
BNXT_TX_STATS_ENTRY(tx_stat_error),
};
static const struct {
long offset;
char string[ETH_GSTRING_LEN];
} bnxt_port_stats_ext_arr[] = {
BNXT_RX_STATS_EXT_ENTRY(link_down_events),
BNXT_RX_STATS_EXT_ENTRY(continuous_pause_events),
BNXT_RX_STATS_EXT_ENTRY(resume_pause_events),
BNXT_RX_STATS_EXT_ENTRY(continuous_roce_pause_events),
BNXT_RX_STATS_EXT_ENTRY(resume_roce_pause_events),
BNXT_RX_STATS_EXT_COS_ENTRIES,
BNXT_RX_STATS_EXT_PFC_ENTRIES,
BNXT_RX_STATS_EXT_ENTRY(rx_bits),
BNXT_RX_STATS_EXT_ENTRY(rx_buffer_passed_threshold),
BNXT_RX_STATS_EXT_ENTRY(rx_pcs_symbol_err),
BNXT_RX_STATS_EXT_ENTRY(rx_corrected_bits),
BNXT_RX_STATS_EXT_DISCARD_COS_ENTRIES,
BNXT_RX_STATS_EXT_ENTRY(rx_fec_corrected_blocks),
BNXT_RX_STATS_EXT_ENTRY(rx_fec_uncorrectable_blocks),
BNXT_RX_STATS_EXT_ENTRY(rx_filter_miss),
};
static const struct {
long offset;
char string[ETH_GSTRING_LEN];
} bnxt_tx_port_stats_ext_arr[] = {
BNXT_TX_STATS_EXT_COS_ENTRIES,
BNXT_TX_STATS_EXT_PFC_ENTRIES,
};
static const struct {
long base_off;
char string[ETH_GSTRING_LEN];
} bnxt_rx_bytes_pri_arr[] = {
BNXT_RX_STATS_PRI_ENTRIES(rx_bytes),
};
static const struct {
long base_off;
char string[ETH_GSTRING_LEN];
} bnxt_rx_pkts_pri_arr[] = {
BNXT_RX_STATS_PRI_ENTRIES(rx_packets),
};
static const struct {
long base_off;
char string[ETH_GSTRING_LEN];
} bnxt_tx_bytes_pri_arr[] = {
BNXT_TX_STATS_PRI_ENTRIES(tx_bytes),
};
static const struct {
long base_off;
char string[ETH_GSTRING_LEN];
} bnxt_tx_pkts_pri_arr[] = {
BNXT_TX_STATS_PRI_ENTRIES(tx_packets),
};
#define BNXT_NUM_RING_ERR_STATS ARRAY_SIZE(bnxt_ring_err_stats_arr)
#define BNXT_NUM_PORT_STATS ARRAY_SIZE(bnxt_port_stats_arr)
#define BNXT_NUM_STATS_PRI \
(ARRAY_SIZE(bnxt_rx_bytes_pri_arr) + \
ARRAY_SIZE(bnxt_rx_pkts_pri_arr) + \
ARRAY_SIZE(bnxt_tx_bytes_pri_arr) + \
ARRAY_SIZE(bnxt_tx_pkts_pri_arr))
static int bnxt_get_num_tpa_ring_stats(struct bnxt *bp)
{
if (BNXT_SUPPORTS_TPA(bp)) {
if (bp->max_tpa_v2) {
if (BNXT_CHIP_P5(bp))
return BNXT_NUM_TPA_RING_STATS_P5;
return BNXT_NUM_TPA_RING_STATS_P7;
}
return BNXT_NUM_TPA_RING_STATS;
}
return 0;
}
static int bnxt_get_num_ring_stats(struct bnxt *bp)
{
int rx, tx, cmn;
rx = NUM_RING_RX_HW_STATS + NUM_RING_RX_SW_STATS +
bnxt_get_num_tpa_ring_stats(bp);
tx = NUM_RING_TX_HW_STATS;
cmn = NUM_RING_CMN_SW_STATS;
return rx * bp->rx_nr_rings +
tx * (bp->tx_nr_rings_xdp + bp->tx_nr_rings_per_tc) +
cmn * bp->cp_nr_rings;
}
static int bnxt_get_num_stats(struct bnxt *bp)
{
int num_stats = bnxt_get_num_ring_stats(bp);
int len;
num_stats += BNXT_NUM_RING_ERR_STATS;
if (bp->flags & BNXT_FLAG_PORT_STATS)
num_stats += BNXT_NUM_PORT_STATS;
if (bp->flags & BNXT_FLAG_PORT_STATS_EXT) {
len = min_t(int , bp->fw_rx_stats_ext_size,
ARRAY_SIZE(bnxt_port_stats_ext_arr));
num_stats += len;
len = min_t(int , bp->fw_tx_stats_ext_size,
ARRAY_SIZE(bnxt_tx_port_stats_ext_arr));
num_stats += len;
if (bp->pri2cos_valid)
num_stats += BNXT_NUM_STATS_PRI;
}
return num_stats;
}
static int bnxt_get_sset_count(struct net_device *dev, int sset)
{
struct bnxt *bp = netdev_priv(dev);
switch (sset) {
case ETH_SS_STATS:
return bnxt_get_num_stats(bp);
case ETH_SS_TEST:
if (!bp->num_tests)
return -EOPNOTSUPP;
return bp->num_tests;
default :
return -EOPNOTSUPP;
}
}
static bool is_rx_ring(struct bnxt *bp, int ring_num)
{
return ring_num < bp->rx_nr_rings;
}
static bool is_tx_ring(struct bnxt *bp, int ring_num)
{
int tx_base = 0;
if (!(bp->flags & BNXT_FLAG_SHARED_RINGS))
tx_base = bp->rx_nr_rings;
if (ring_num >= tx_base && ring_num < (tx_base + bp->tx_nr_rings))
return true ;
return false ;
}
static void bnxt_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *buf)
{
struct bnxt_total_ring_err_stats ring_err_stats = {0};
struct bnxt *bp = netdev_priv(dev);
u64 *curr, *prev;
u32 tpa_stats;
u32 i, j = 0;
if (!bp->bnapi) {
j += bnxt_get_num_ring_stats(bp);
goto skip_ring_stats;
}
tpa_stats = bnxt_get_num_tpa_ring_stats(bp);
for (i = 0; i < bp->cp_nr_rings; i++) {
struct bnxt_napi *bnapi = bp->bnapi[i];
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
u64 *sw_stats = cpr->stats.sw_stats;
u64 *sw;
int k;
if (is_rx_ring(bp, i)) {
for (k = 0; k < NUM_RING_RX_HW_STATS; j++, k++)
buf[j] = sw_stats[k];
}
if (is_tx_ring(bp, i)) {
k = NUM_RING_RX_HW_STATS;
for (; k < NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS;
j++, k++)
buf[j] = sw_stats[k];
}
if (!tpa_stats || !is_rx_ring(bp, i))
goto skip_tpa_ring_stats;
k = NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS;
for (; k < NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS +
tpa_stats; j++, k++)
buf[j] = sw_stats[k];
skip_tpa_ring_stats:
sw = (u64 *)&cpr->sw_stats->rx;
if (is_rx_ring(bp, i)) {
for (k = 0; k < NUM_RING_RX_SW_STATS; j++, k++)
buf[j] = sw[k];
}
sw = (u64 *)&cpr->sw_stats->cmn;
for (k = 0; k < NUM_RING_CMN_SW_STATS; j++, k++)
buf[j] = sw[k];
}
bnxt_get_ring_err_stats(bp, &ring_err_stats);
skip_ring_stats:
curr = &ring_err_stats.rx_total_l4_csum_errors;
prev = &bp->ring_err_stats_prev.rx_total_l4_csum_errors;
for (i = 0; i < BNXT_NUM_RING_ERR_STATS; i++, j++, curr++, prev++)
buf[j] = *curr + *prev;
if (bp->flags & BNXT_FLAG_PORT_STATS) {
u64 *port_stats = bp->port_stats.sw_stats;
for (i = 0; i < BNXT_NUM_PORT_STATS; i++, j++)
buf[j] = *(port_stats + bnxt_port_stats_arr[i].offset);
}
if (bp->flags & BNXT_FLAG_PORT_STATS_EXT) {
u64 *rx_port_stats_ext = bp->rx_port_stats_ext.sw_stats;
u64 *tx_port_stats_ext = bp->tx_port_stats_ext.sw_stats;
u32 len;
len = min_t(u32, bp->fw_rx_stats_ext_size,
ARRAY_SIZE(bnxt_port_stats_ext_arr));
for (i = 0; i < len; i++, j++) {
buf[j] = *(rx_port_stats_ext +
bnxt_port_stats_ext_arr[i].offset);
}
len = min_t(u32, bp->fw_tx_stats_ext_size,
ARRAY_SIZE(bnxt_tx_port_stats_ext_arr));
for (i = 0; i < len; i++, j++) {
buf[j] = *(tx_port_stats_ext +
bnxt_tx_port_stats_ext_arr[i].offset);
}
if (bp->pri2cos_valid) {
for (i = 0; i < 8; i++, j++) {
long n = bnxt_rx_bytes_pri_arr[i].base_off +
bp->pri2cos_idx[i];
buf[j] = *(rx_port_stats_ext + n);
}
for (i = 0; i < 8; i++, j++) {
long n = bnxt_rx_pkts_pri_arr[i].base_off +
bp->pri2cos_idx[i];
buf[j] = *(rx_port_stats_ext + n);
}
for (i = 0; i < 8; i++, j++) {
long n = bnxt_tx_bytes_pri_arr[i].base_off +
bp->pri2cos_idx[i];
buf[j] = *(tx_port_stats_ext + n);
}
for (i = 0; i < 8; i++, j++) {
long n = bnxt_tx_pkts_pri_arr[i].base_off +
bp->pri2cos_idx[i];
buf[j] = *(tx_port_stats_ext + n);
}
}
}
}
static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
{
struct bnxt *bp = netdev_priv(dev);
u32 i, j, num_str;
const char *str;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < bp->cp_nr_rings; i++) {
if (is_rx_ring(bp, i))
for (j = 0; j < NUM_RING_RX_HW_STATS; j++) {
str = bnxt_ring_rx_stats_str[j];
ethtool_sprintf(&buf, "[%d]: %s" , i,
str);
}
if (is_tx_ring(bp, i))
for (j = 0; j < NUM_RING_TX_HW_STATS; j++) {
str = bnxt_ring_tx_stats_str[j];
ethtool_sprintf(&buf, "[%d]: %s" , i,
str);
}
num_str = bnxt_get_num_tpa_ring_stats(bp);
if (!num_str || !is_rx_ring(bp, i))
goto skip_tpa_stats;
if (bp->max_tpa_v2)
for (j = 0; j < num_str; j++) {
str = bnxt_ring_tpa2_stats_str[j];
ethtool_sprintf(&buf, "[%d]: %s" , i,
str);
}
else
for (j = 0; j < num_str; j++) {
str = bnxt_ring_tpa_stats_str[j];
ethtool_sprintf(&buf, "[%d]: %s" , i,
str);
}
skip_tpa_stats:
if (is_rx_ring(bp, i))
for (j = 0; j < NUM_RING_RX_SW_STATS; j++) {
str = bnxt_rx_sw_stats_str[j];
ethtool_sprintf(&buf, "[%d]: %s" , i,
str);
}
for (j = 0; j < NUM_RING_CMN_SW_STATS; j++) {
str = bnxt_cmn_sw_stats_str[j];
ethtool_sprintf(&buf, "[%d]: %s" , i, str);
}
}
for (i = 0; i < BNXT_NUM_RING_ERR_STATS; i++)
ethtool_puts(&buf, bnxt_ring_err_stats_arr[i]);
if (bp->flags & BNXT_FLAG_PORT_STATS)
for (i = 0; i < BNXT_NUM_PORT_STATS; i++) {
str = bnxt_port_stats_arr[i].string;
ethtool_puts(&buf, str);
}
if (bp->flags & BNXT_FLAG_PORT_STATS_EXT) {
u32 len;
len = min_t(u32, bp->fw_rx_stats_ext_size,
ARRAY_SIZE(bnxt_port_stats_ext_arr));
for (i = 0; i < len; i++) {
str = bnxt_port_stats_ext_arr[i].string;
ethtool_puts(&buf, str);
}
len = min_t(u32, bp->fw_tx_stats_ext_size,
ARRAY_SIZE(bnxt_tx_port_stats_ext_arr));
for (i = 0; i < len; i++) {
str = bnxt_tx_port_stats_ext_arr[i].string;
ethtool_puts(&buf, str);
}
if (bp->pri2cos_valid) {
for (i = 0; i < 8; i++) {
str = bnxt_rx_bytes_pri_arr[i].string;
ethtool_puts(&buf, str);
}
for (i = 0; i < 8; i++) {
str = bnxt_rx_pkts_pri_arr[i].string;
ethtool_puts(&buf, str);
}
for (i = 0; i < 8; i++) {
str = bnxt_tx_bytes_pri_arr[i].string;
ethtool_puts(&buf, str);
}
for (i = 0; i < 8; i++) {
str = bnxt_tx_pkts_pri_arr[i].string;
ethtool_puts(&buf, str);
}
}
}
break ;
case ETH_SS_TEST:
if (bp->num_tests)
for (i = 0; i < bp->num_tests; i++)
ethtool_puts(&buf, bp->test_info->string[i]);
break ;
default :
netdev_err(bp->dev, "bnxt_get_strings invalid request %x\n" ,
stringset);
break ;
}
}
static void bnxt_get_ringparam(struct net_device *dev,
struct ethtool_ringparam *ering,
struct kernel_ethtool_ringparam *kernel_ering,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
if (bp->flags & BNXT_FLAG_AGG_RINGS) {
ering->rx_max_pending = BNXT_MAX_RX_DESC_CNT_JUM_ENA;
ering->rx_jumbo_max_pending = BNXT_MAX_RX_JUM_DESC_CNT;
kernel_ering->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_ENABLED;
} else {
ering->rx_max_pending = BNXT_MAX_RX_DESC_CNT;
ering->rx_jumbo_max_pending = 0;
kernel_ering->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_DISABLED;
}
ering->tx_max_pending = BNXT_MAX_TX_DESC_CNT;
ering->rx_pending = bp->rx_ring_size;
ering->rx_jumbo_pending = bp->rx_agg_ring_size;
ering->tx_pending = bp->tx_ring_size;
kernel_ering->hds_thresh_max = BNXT_HDS_THRESHOLD_MAX;
}
static int bnxt_set_ringparam(struct net_device *dev,
struct ethtool_ringparam *ering,
struct kernel_ethtool_ringparam *kernel_ering,
struct netlink_ext_ack *extack)
{
u8 tcp_data_split = kernel_ering->tcp_data_split;
struct bnxt *bp = netdev_priv(dev);
u8 hds_config_mod;
if ((ering->rx_pending > BNXT_MAX_RX_DESC_CNT) ||
(ering->tx_pending > BNXT_MAX_TX_DESC_CNT) ||
(ering->tx_pending < BNXT_MIN_TX_DESC_CNT))
return -EINVAL;
hds_config_mod = tcp_data_split != dev->cfg->hds_config;
if (tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_DISABLED && hds_config_mod)
return -EINVAL;
if (tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_ENABLED &&
hds_config_mod && BNXT_RX_PAGE_MODE(bp)) {
NL_SET_ERR_MSG_MOD(extack, "tcp-data-split is disallowed when XDP is attached" );
return -EINVAL;
}
if (netif_running(dev))
bnxt_close_nic(bp, false , false );
if (hds_config_mod) {
if (tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_ENABLED)
bp->flags |= BNXT_FLAG_HDS;
else if (tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_UNKNOWN)
bp->flags &= ~BNXT_FLAG_HDS;
}
bp->rx_ring_size = ering->rx_pending;
bp->tx_ring_size = ering->tx_pending;
bnxt_set_ring_params(bp);
if (netif_running(dev))
return bnxt_open_nic(bp, false , false );
return 0;
}
static void bnxt_get_channels(struct net_device *dev,
struct ethtool_channels *channel)
{
struct bnxt *bp = netdev_priv(dev);
struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
int max_rx_rings, max_tx_rings, tcs;
int max_tx_sch_inputs, tx_grps;
/* Get the most up-to-date max_tx_sch_inputs. */
if (netif_running(dev) && BNXT_NEW_RM(bp))
bnxt_hwrm_func_resc_qcaps(bp, false );
max_tx_sch_inputs = hw_resc->max_tx_sch_inputs;
bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true );
if (max_tx_sch_inputs)
max_tx_rings = min_t(int , max_tx_rings, max_tx_sch_inputs);
tcs = bp->num_tc;
tx_grps = max(tcs, 1);
if (bp->tx_nr_rings_xdp)
tx_grps++;
max_tx_rings /= tx_grps;
channel->max_combined = min_t(int , max_rx_rings, max_tx_rings);
if (bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false )) {
max_rx_rings = 0;
max_tx_rings = 0;
}
if (max_tx_sch_inputs)
max_tx_rings = min_t(int , max_tx_rings, max_tx_sch_inputs);
if (tcs > 1)
max_tx_rings /= tcs;
channel->max_rx = max_rx_rings;
channel->max_tx = max_tx_rings;
channel->max_other = 0;
if (bp->flags & BNXT_FLAG_SHARED_RINGS) {
channel->combined_count = bp->rx_nr_rings;
if (BNXT_CHIP_TYPE_NITRO_A0(bp))
channel->combined_count--;
} else {
if (!BNXT_CHIP_TYPE_NITRO_A0(bp)) {
channel->rx_count = bp->rx_nr_rings;
channel->tx_count = bp->tx_nr_rings_per_tc;
}
}
}
static int bnxt_set_channels(struct net_device *dev,
struct ethtool_channels *channel)
{
struct bnxt *bp = netdev_priv(dev);
int req_tx_rings, req_rx_rings, tcs;
bool sh = false ;
int tx_xdp = 0;
int rc = 0;
int tx_cp;
if (channel->other_count)
return -EINVAL;
if (!channel->combined_count &&
(!channel->rx_count || !channel->tx_count))
return -EINVAL;
if (channel->combined_count &&
(channel->rx_count || channel->tx_count))
return -EINVAL;
if (BNXT_CHIP_TYPE_NITRO_A0(bp) && (channel->rx_count ||
channel->tx_count))
return -EINVAL;
if (channel->combined_count)
sh = true ;
tcs = bp->num_tc;
req_tx_rings = sh ? channel->combined_count : channel->tx_count;
req_rx_rings = sh ? channel->combined_count : channel->rx_count;
if (bp->tx_nr_rings_xdp) {
if (!sh) {
netdev_err(dev, "Only combined mode supported when XDP is enabled.\n" );
return -EINVAL;
}
tx_xdp = req_rx_rings;
}
if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) !=
bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) &&
netif_is_rxfh_configured(dev)) {
netdev_warn(dev, "RSS table size change required, RSS table entries must be default to proceed\n" );
return -EINVAL;
}
rc = bnxt_check_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, tx_xdp);
if (rc) {
netdev_warn(dev, "Unable to allocate the requested rings\n" );
return rc;
}
if (netif_running(dev)) {
if (BNXT_PF(bp)) {
/* TODO CHIMP_FW: Send message to all VF's
* before PF unload
*/
}
bnxt_close_nic(bp, true , false );
}
if (sh) {
bp->flags |= BNXT_FLAG_SHARED_RINGS;
bp->rx_nr_rings = channel->combined_count;
bp->tx_nr_rings_per_tc = channel->combined_count;
} else {
bp->flags &= ~BNXT_FLAG_SHARED_RINGS;
bp->rx_nr_rings = channel->rx_count;
bp->tx_nr_rings_per_tc = channel->tx_count;
}
bp->tx_nr_rings_xdp = tx_xdp;
bp->tx_nr_rings = bp->tx_nr_rings_per_tc + tx_xdp;
if (tcs > 1)
bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs + tx_xdp;
tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings);
bp->cp_nr_rings = sh ? max_t(int , tx_cp, bp->rx_nr_rings) :
tx_cp + bp->rx_nr_rings;
/* After changing number of rx channels, update NTUPLE feature. */
netdev_update_features(dev);
if (netif_running(dev)) {
rc = bnxt_open_nic(bp, true , false );
if ((!rc) && BNXT_PF(bp)) {
/* TODO CHIMP_FW: Send message to all VF's
* to renable
*/
}
} else {
rc = bnxt_reserve_rings(bp, true );
}
return rc;
}
static u32 bnxt_get_all_fltr_ids_rcu(struct bnxt *bp, struct hlist_head tbl[],
int tbl_size, u32 *ids, u32 start,
u32 id_cnt)
{
int i, j = start;
if (j >= id_cnt)
return j;
for (i = 0; i < tbl_size; i++) {
struct hlist_head *head;
struct bnxt_filter_base *fltr;
head = &tbl[i];
hlist_for_each_entry_rcu(fltr, head, hash) {
if (!fltr->flags ||
test_bit(BNXT_FLTR_FW_DELETED, &fltr->state))
continue ;
ids[j++] = fltr->sw_id;
if (j == id_cnt)
return j;
}
}
return j;
}
static struct bnxt_filter_base *bnxt_get_one_fltr_rcu(struct bnxt *bp,
struct hlist_head tbl[],
int tbl_size, u32 id)
{
int i;
for (i = 0; i < tbl_size; i++) {
struct hlist_head *head;
struct bnxt_filter_base *fltr;
head = &tbl[i];
hlist_for_each_entry_rcu(fltr, head, hash) {
if (fltr->flags && fltr->sw_id == id)
return fltr;
}
}
return NULL;
}
static int bnxt_grxclsrlall(struct bnxt *bp, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
u32 count;
cmd->data = bp->ntp_fltr_count;
rcu_read_lock();
count = bnxt_get_all_fltr_ids_rcu(bp, bp->l2_fltr_hash_tbl,
BNXT_L2_FLTR_HASH_SIZE, rule_locs, 0,
cmd->rule_cnt);
cmd->rule_cnt = bnxt_get_all_fltr_ids_rcu(bp, bp->ntp_fltr_hash_tbl,
BNXT_NTP_FLTR_HASH_SIZE,
rule_locs, count,
cmd->rule_cnt);
rcu_read_unlock();
return 0;
}
static int bnxt_grxclsrule(struct bnxt *bp, struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fs =
(struct ethtool_rx_flow_spec *)&cmd->fs;
struct bnxt_filter_base *fltr_base;
struct bnxt_ntuple_filter *fltr;
struct bnxt_flow_masks *fmasks;
struct flow_keys *fkeys;
int rc = -EINVAL;
if (fs->location >= bp->max_fltr)
return rc;
rcu_read_lock();
fltr_base = bnxt_get_one_fltr_rcu(bp, bp->l2_fltr_hash_tbl,
BNXT_L2_FLTR_HASH_SIZE,
fs->location);
if (fltr_base) {
struct ethhdr *h_ether = &fs->h_u.ether_spec;
struct ethhdr *m_ether = &fs->m_u.ether_spec;
struct bnxt_l2_filter *l2_fltr;
struct bnxt_l2_key *l2_key;
l2_fltr = container_of(fltr_base, struct bnxt_l2_filter, base);
l2_key = &l2_fltr->l2_key;
fs->flow_type = ETHER_FLOW;
ether_addr_copy(h_ether->h_dest, l2_key->dst_mac_addr);
eth_broadcast_addr(m_ether->h_dest);
if (l2_key->vlan) {
struct ethtool_flow_ext *m_ext = &fs->m_ext;
struct ethtool_flow_ext *h_ext = &fs->h_ext;
fs->flow_type |= FLOW_EXT;
m_ext->vlan_tci = htons(0xfff);
h_ext->vlan_tci = htons(l2_key->vlan);
}
if (fltr_base->flags & BNXT_ACT_RING_DST)
fs->ring_cookie = fltr_base->rxq;
if (fltr_base->flags & BNXT_ACT_FUNC_DST)
fs->ring_cookie = (u64)(fltr_base->vf_idx + 1) <<
ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
rcu_read_unlock();
return 0;
}
fltr_base = bnxt_get_one_fltr_rcu(bp, bp->ntp_fltr_hash_tbl,
BNXT_NTP_FLTR_HASH_SIZE,
fs->location);
if (!fltr_base) {
rcu_read_unlock();
return rc;
}
fltr = container_of(fltr_base, struct bnxt_ntuple_filter, base);
fkeys = &fltr->fkeys;
fmasks = &fltr->fmasks;
if (fkeys->basic.n_proto == htons(ETH_P_IP)) {
if (fkeys->basic.ip_proto == BNXT_IP_PROTO_WILDCARD) {
fs->flow_type = IP_USER_FLOW;
fs->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
fs->h_u.usr_ip4_spec.proto = BNXT_IP_PROTO_WILDCARD;
fs->m_u.usr_ip4_spec.proto = 0;
} else if (fkeys->basic.ip_proto == IPPROTO_ICMP) {
fs->flow_type = IP_USER_FLOW;
fs->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
fs->h_u.usr_ip4_spec.proto = IPPROTO_ICMP;
fs->m_u.usr_ip4_spec.proto = BNXT_IP_PROTO_FULL_MASK;
} else if (fkeys->basic.ip_proto == IPPROTO_TCP) {
fs->flow_type = TCP_V4_FLOW;
} else if (fkeys->basic.ip_proto == IPPROTO_UDP) {
fs->flow_type = UDP_V4_FLOW;
} else {
goto fltr_err;
}
fs->h_u.tcp_ip4_spec.ip4src = fkeys->addrs.v4addrs.src;
fs->m_u.tcp_ip4_spec.ip4src = fmasks->addrs.v4addrs.src;
fs->h_u.tcp_ip4_spec.ip4dst = fkeys->addrs.v4addrs.dst;
fs->m_u.tcp_ip4_spec.ip4dst = fmasks->addrs.v4addrs.dst;
if (fs->flow_type == TCP_V4_FLOW ||
fs->flow_type == UDP_V4_FLOW) {
fs->h_u.tcp_ip4_spec.psrc = fkeys->ports.src;
fs->m_u.tcp_ip4_spec.psrc = fmasks->ports.src;
fs->h_u.tcp_ip4_spec.pdst = fkeys->ports.dst;
fs->m_u.tcp_ip4_spec.pdst = fmasks->ports.dst;
}
} else {
if (fkeys->basic.ip_proto == BNXT_IP_PROTO_WILDCARD) {
fs->flow_type = IPV6_USER_FLOW;
fs->h_u.usr_ip6_spec.l4_proto = BNXT_IP_PROTO_WILDCARD;
fs->m_u.usr_ip6_spec.l4_proto = 0;
} else if (fkeys->basic.ip_proto == IPPROTO_ICMPV6) {
fs->flow_type = IPV6_USER_FLOW;
fs->h_u.usr_ip6_spec.l4_proto = IPPROTO_ICMPV6;
fs->m_u.usr_ip6_spec.l4_proto = BNXT_IP_PROTO_FULL_MASK;
} else if (fkeys->basic.ip_proto == IPPROTO_TCP) {
fs->flow_type = TCP_V6_FLOW;
} else if (fkeys->basic.ip_proto == IPPROTO_UDP) {
fs->flow_type = UDP_V6_FLOW;
} else {
goto fltr_err;
}
*(struct in6_addr *)&fs->h_u.tcp_ip6_spec.ip6src[0] =
fkeys->addrs.v6addrs.src;
*(struct in6_addr *)&fs->m_u.tcp_ip6_spec.ip6src[0] =
fmasks->addrs.v6addrs.src;
*(struct in6_addr *)&fs->h_u.tcp_ip6_spec.ip6dst[0] =
fkeys->addrs.v6addrs.dst;
*(struct in6_addr *)&fs->m_u.tcp_ip6_spec.ip6dst[0] =
fmasks->addrs.v6addrs.dst;
if (fs->flow_type == TCP_V6_FLOW ||
fs->flow_type == UDP_V6_FLOW) {
fs->h_u.tcp_ip6_spec.psrc = fkeys->ports.src;
fs->m_u.tcp_ip6_spec.psrc = fmasks->ports.src;
fs->h_u.tcp_ip6_spec.pdst = fkeys->ports.dst;
fs->m_u.tcp_ip6_spec.pdst = fmasks->ports.dst;
}
}
if (fltr->base.flags & BNXT_ACT_DROP) {
fs->ring_cookie = RX_CLS_FLOW_DISC;
} else if (fltr->base.flags & BNXT_ACT_RSS_CTX) {
fs->flow_type |= FLOW_RSS;
cmd->rss_context = fltr->base.fw_vnic_id;
} else {
fs->ring_cookie = fltr->base.rxq;
}
rc = 0;
fltr_err:
rcu_read_unlock();
return rc;
}
static struct bnxt_rss_ctx *bnxt_get_rss_ctx_from_index(struct bnxt *bp,
u32 index)
{
struct ethtool_rxfh_context *ctx;
ctx = xa_load(&bp->dev->ethtool->rss_ctx, index);
if (!ctx)
return NULL;
return ethtool_rxfh_context_priv(ctx);
}
static int bnxt_alloc_vnic_rss_table(struct bnxt *bp,
struct bnxt_vnic_info *vnic)
{
int size = L1_CACHE_ALIGN(BNXT_MAX_RSS_TABLE_SIZE_P5);
vnic->rss_table_size = size + HW_HASH_KEY_SIZE;
vnic->rss_table = dma_alloc_coherent(&bp->pdev->dev,
vnic->rss_table_size,
&vnic->rss_table_dma_addr,
GFP_KERNEL);
if (!vnic->rss_table)
return -ENOMEM;
vnic->rss_hash_key = ((void *)vnic->rss_table) + size;
vnic->rss_hash_key_dma_addr = vnic->rss_table_dma_addr + size;
return 0;
}
static int bnxt_add_l2_cls_rule(struct bnxt *bp,
struct ethtool_rx_flow_spec *fs)
{
u32 ring = ethtool_get_flow_spec_ring(fs->ring_cookie);
u8 vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
struct ethhdr *h_ether = &fs->h_u.ether_spec;
struct ethhdr *m_ether = &fs->m_u.ether_spec;
struct bnxt_l2_filter *fltr;
struct bnxt_l2_key key;
u16 vnic_id;
u8 flags;
int rc;
if (BNXT_CHIP_P5_PLUS(bp))
return -EOPNOTSUPP;
if (!is_broadcast_ether_addr(m_ether->h_dest))
return -EINVAL;
ether_addr_copy(key.dst_mac_addr, h_ether->h_dest);
key.vlan = 0;
if (fs->flow_type & FLOW_EXT) {
struct ethtool_flow_ext *m_ext = &fs->m_ext;
struct ethtool_flow_ext *h_ext = &fs->h_ext;
if (m_ext->vlan_tci != htons(0xfff) || !h_ext->vlan_tci)
return -EINVAL;
key.vlan = ntohs(h_ext->vlan_tci);
}
if (vf) {
flags = BNXT_ACT_FUNC_DST;
vnic_id = 0xffff;
vf--;
} else {
flags = BNXT_ACT_RING_DST;
vnic_id = bp->vnic_info[ring + 1].fw_vnic_id;
}
fltr = bnxt_alloc_new_l2_filter(bp, &key, flags);
if (IS_ERR(fltr))
return PTR_ERR(fltr);
fltr->base.fw_vnic_id = vnic_id;
fltr->base.rxq = ring;
fltr->base.vf_idx = vf;
rc = bnxt_hwrm_l2_filter_alloc(bp, fltr);
if (rc)
bnxt_del_l2_filter(bp, fltr);
else
fs->location = fltr->base.sw_id;
return rc;
}
static bool bnxt_verify_ntuple_ip4_flow(struct ethtool_usrip4_spec *ip_spec,
struct ethtool_usrip4_spec *ip_mask)
{
u8 mproto = ip_mask->proto;
u8 sproto = ip_spec->proto;
if (ip_mask->l4_4_bytes || ip_mask->tos ||
ip_spec->ip_ver != ETH_RX_NFC_IP4 ||
(mproto && (mproto != BNXT_IP_PROTO_FULL_MASK || sproto != IPPROTO_ICMP)))
return false ;
return true ;
}
static bool bnxt_verify_ntuple_ip6_flow(struct ethtool_usrip6_spec *ip_spec,
struct ethtool_usrip6_spec *ip_mask)
{
u8 mproto = ip_mask->l4_proto;
u8 sproto = ip_spec->l4_proto;
if (ip_mask->l4_4_bytes || ip_mask->tclass ||
(mproto && (mproto != BNXT_IP_PROTO_FULL_MASK || sproto != IPPROTO_ICMPV6)))
return false ;
return true ;
}
static int bnxt_add_ntuple_cls_rule(struct bnxt *bp,
struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fs = &cmd->fs;
struct bnxt_ntuple_filter *new_fltr, *fltr;
u32 flow_type = fs->flow_type & 0xff;
struct bnxt_l2_filter *l2_fltr;
struct bnxt_flow_masks *fmasks;
struct flow_keys *fkeys;
u32 idx, ring;
int rc;
u8 vf;
if (!bp->vnic_info)
return -EAGAIN;
vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
ring = ethtool_get_flow_spec_ring(fs->ring_cookie);
if ((fs->flow_type & (FLOW_MAC_EXT | FLOW_EXT)) || vf)
return -EOPNOTSUPP;
if (flow_type == IP_USER_FLOW) {
if (!bnxt_verify_ntuple_ip4_flow(&fs->h_u.usr_ip4_spec,
&fs->m_u.usr_ip4_spec))
return -EOPNOTSUPP;
}
if (flow_type == IPV6_USER_FLOW) {
if (!bnxt_verify_ntuple_ip6_flow(&fs->h_u.usr_ip6_spec,
&fs->m_u.usr_ip6_spec))
return -EOPNOTSUPP;
}
new_fltr = kzalloc(sizeof (*new_fltr), GFP_KERNEL);
if (!new_fltr)
return -ENOMEM;
l2_fltr = bp->vnic_info[BNXT_VNIC_DEFAULT].l2_filters[0];
atomic_inc(&l2_fltr->refcnt);
new_fltr->l2_fltr = l2_fltr;
fmasks = &new_fltr->fmasks;
fkeys = &new_fltr->fkeys;
rc = -EOPNOTSUPP;
switch (flow_type) {
case IP_USER_FLOW: {
struct ethtool_usrip4_spec *ip_spec = &fs->h_u.usr_ip4_spec;
struct ethtool_usrip4_spec *ip_mask = &fs->m_u.usr_ip4_spec;
fkeys->basic.ip_proto = ip_mask->proto ? ip_spec->proto
: BNXT_IP_PROTO_WILDCARD;
fkeys->basic.n_proto = htons(ETH_P_IP);
fkeys->addrs.v4addrs.src = ip_spec->ip4src;
fmasks->addrs.v4addrs.src = ip_mask->ip4src;
fkeys->addrs.v4addrs.dst = ip_spec->ip4dst;
fmasks->addrs.v4addrs.dst = ip_mask->ip4dst;
break ;
}
case TCP_V4_FLOW:
case UDP_V4_FLOW: {
struct ethtool_tcpip4_spec *ip_spec = &fs->h_u.tcp_ip4_spec;
struct ethtool_tcpip4_spec *ip_mask = &fs->m_u.tcp_ip4_spec;
fkeys->basic.ip_proto = IPPROTO_TCP;
if (flow_type == UDP_V4_FLOW)
fkeys->basic.ip_proto = IPPROTO_UDP;
fkeys->basic.n_proto = htons(ETH_P_IP);
fkeys->addrs.v4addrs.src = ip_spec->ip4src;
fmasks->addrs.v4addrs.src = ip_mask->ip4src;
fkeys->addrs.v4addrs.dst = ip_spec->ip4dst;
fmasks->addrs.v4addrs.dst = ip_mask->ip4dst;
fkeys->ports.src = ip_spec->psrc;
fmasks->ports.src = ip_mask->psrc;
fkeys->ports.dst = ip_spec->pdst;
fmasks->ports.dst = ip_mask->pdst;
break ;
}
case IPV6_USER_FLOW: {
struct ethtool_usrip6_spec *ip_spec = &fs->h_u.usr_ip6_spec;
struct ethtool_usrip6_spec *ip_mask = &fs->m_u.usr_ip6_spec;
fkeys->basic.ip_proto = ip_mask->l4_proto ? ip_spec->l4_proto
: BNXT_IP_PROTO_WILDCARD;
fkeys->basic.n_proto = htons(ETH_P_IPV6);
fkeys->addrs.v6addrs.src = *(struct in6_addr *)&ip_spec->ip6src;
fmasks->addrs.v6addrs.src = *(struct in6_addr *)&ip_mask->ip6src;
fkeys->addrs.v6addrs.dst = *(struct in6_addr *)&ip_spec->ip6dst;
fmasks->addrs.v6addrs.dst = *(struct in6_addr *)&ip_mask->ip6dst;
break ;
}
case TCP_V6_FLOW:
case UDP_V6_FLOW: {
struct ethtool_tcpip6_spec *ip_spec = &fs->h_u.tcp_ip6_spec;
struct ethtool_tcpip6_spec *ip_mask = &fs->m_u.tcp_ip6_spec;
fkeys->basic.ip_proto = IPPROTO_TCP;
if (flow_type == UDP_V6_FLOW)
fkeys->basic.ip_proto = IPPROTO_UDP;
fkeys->basic.n_proto = htons(ETH_P_IPV6);
fkeys->addrs.v6addrs.src = *(struct in6_addr *)&ip_spec->ip6src;
fmasks->addrs.v6addrs.src = *(struct in6_addr *)&ip_mask->ip6src;
fkeys->addrs.v6addrs.dst = *(struct in6_addr *)&ip_spec->ip6dst;
fmasks->addrs.v6addrs.dst = *(struct in6_addr *)&ip_mask->ip6dst;
fkeys->ports.src = ip_spec->psrc;
fmasks->ports.src = ip_mask->psrc;
fkeys->ports.dst = ip_spec->pdst;
fmasks->ports.dst = ip_mask->pdst;
break ;
}
default :
rc = -EOPNOTSUPP;
goto ntuple_err;
}
if (!memcmp(&BNXT_FLOW_MASK_NONE, fmasks, sizeof (*fmasks)))
goto ntuple_err;
idx = bnxt_get_ntp_filter_idx(bp, fkeys, NULL);
rcu_read_lock();
fltr = bnxt_lookup_ntp_filter_from_idx(bp, new_fltr, idx);
if (fltr) {
rcu_read_unlock();
rc = -EEXIST;
goto ntuple_err;
}
rcu_read_unlock();
new_fltr->base.flags = BNXT_ACT_NO_AGING;
if (fs->flow_type & FLOW_RSS) {
struct bnxt_rss_ctx *rss_ctx;
new_fltr->base.fw_vnic_id = 0;
new_fltr->base.flags |= BNXT_ACT_RSS_CTX;
rss_ctx = bnxt_get_rss_ctx_from_index(bp, cmd->rss_context);
if (rss_ctx) {
new_fltr->base.fw_vnic_id = rss_ctx->index;
} else {
rc = -EINVAL;
goto ntuple_err;
}
}
if (fs->ring_cookie == RX_CLS_FLOW_DISC)
new_fltr->base.flags |= BNXT_ACT_DROP;
else
new_fltr->base.rxq = ring;
__set_bit(BNXT_FLTR_VALID, &new_fltr->base.state);
rc = bnxt_insert_ntp_filter(bp, new_fltr, idx);
if (!rc) {
rc = bnxt_hwrm_cfa_ntuple_filter_alloc(bp, new_fltr);
if (rc) {
bnxt_del_ntp_filter(bp, new_fltr);
return rc;
}
fs->location = new_fltr->base.sw_id;
return 0;
}
ntuple_err:
atomic_dec(&l2_fltr->refcnt);
kfree(new_fltr);
return rc;
}
static int bnxt_srxclsrlins(struct bnxt *bp, struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fs = &cmd->fs;
u32 ring, flow_type;
int rc;
u8 vf;
if (!netif_running(bp->dev))
return -EAGAIN;
if (!(bp->flags & BNXT_FLAG_RFS))
return -EPERM;
if (fs->location != RX_CLS_LOC_ANY)
return -EINVAL;
flow_type = fs->flow_type;
if ((flow_type == IP_USER_FLOW ||
flow_type == IPV6_USER_FLOW) &&
!(bp->fw_cap & BNXT_FW_CAP_CFA_NTUPLE_RX_EXT_IP_PROTO))
return -EOPNOTSUPP;
if (flow_type & FLOW_MAC_EXT)
return -EINVAL;
flow_type &= ~FLOW_EXT;
if (fs->ring_cookie == RX_CLS_FLOW_DISC && flow_type != ETHER_FLOW)
return bnxt_add_ntuple_cls_rule(bp, cmd);
ring = ethtool_get_flow_spec_ring(fs->ring_cookie);
vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
if (BNXT_VF(bp) && vf)
return -EINVAL;
if (BNXT_PF(bp) && vf > bp->pf.active_vfs)
return -EINVAL;
if (!vf && ring >= bp->rx_nr_rings)
return -EINVAL;
if (flow_type == ETHER_FLOW)
rc = bnxt_add_l2_cls_rule(bp, fs);
else
rc = bnxt_add_ntuple_cls_rule(bp, cmd);
return rc;
}
static int bnxt_srxclsrldel(struct bnxt *bp, struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fs = &cmd->fs;
struct bnxt_filter_base *fltr_base;
struct bnxt_ntuple_filter *fltr;
u32 id = fs->location;
rcu_read_lock();
fltr_base = bnxt_get_one_fltr_rcu(bp, bp->l2_fltr_hash_tbl,
BNXT_L2_FLTR_HASH_SIZE, id);
if (fltr_base) {
struct bnxt_l2_filter *l2_fltr;
l2_fltr = container_of(fltr_base, struct bnxt_l2_filter, base);
rcu_read_unlock();
bnxt_hwrm_l2_filter_free(bp, l2_fltr);
bnxt_del_l2_filter(bp, l2_fltr);
return 0;
}
fltr_base = bnxt_get_one_fltr_rcu(bp, bp->ntp_fltr_hash_tbl,
BNXT_NTP_FLTR_HASH_SIZE, id);
if (!fltr_base) {
rcu_read_unlock();
return -ENOENT;
}
fltr = container_of(fltr_base, struct bnxt_ntuple_filter, base);
if (!(fltr->base.flags & BNXT_ACT_NO_AGING)) {
rcu_read_unlock();
return -EINVAL;
}
rcu_read_unlock();
bnxt_hwrm_cfa_ntuple_filter_free(bp, fltr);
bnxt_del_ntp_filter(bp, fltr);
return 0;
}
static u64 get_ethtool_ipv4_rss(struct bnxt *bp)
{
if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4)
return RXH_IP_SRC | RXH_IP_DST;
return 0;
}
static u64 get_ethtool_ipv6_rss(struct bnxt *bp)
{
if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6)
return RXH_IP_SRC | RXH_IP_DST;
return 0;
}
static int bnxt_get_rxfh_fields(struct net_device *dev,
struct ethtool_rxfh_fields *cmd)
{
struct bnxt *bp = netdev_priv(dev);
cmd->data = 0;
switch (cmd->flow_type) {
case TCP_V4_FLOW:
if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4)
cmd->data |= RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3;
cmd->data |= get_ethtool_ipv4_rss(bp);
break ;
case UDP_V4_FLOW:
if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4)
cmd->data |= RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3;
fallthrough;
case AH_ESP_V4_FLOW:
if (bp->rss_hash_cfg &
(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |
VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4))
cmd->data |= RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3;
fallthrough;
case SCTP_V4_FLOW:
case AH_V4_FLOW:
case ESP_V4_FLOW:
case IPV4_FLOW:
cmd->data |= get_ethtool_ipv4_rss(bp);
break ;
case TCP_V6_FLOW:
if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6)
cmd->data |= RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3;
cmd->data |= get_ethtool_ipv6_rss(bp);
break ;
case UDP_V6_FLOW:
if (bp->rss_hash_cfg & VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6)
cmd->data |= RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3;
fallthrough;
case AH_ESP_V6_FLOW:
if (bp->rss_hash_cfg &
(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |
VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6))
cmd->data |= RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3;
fallthrough;
case SCTP_V6_FLOW:
case AH_V6_FLOW:
case ESP_V6_FLOW:
case IPV6_FLOW:
cmd->data |= get_ethtool_ipv6_rss(bp);
break ;
}
return 0;
}
#define RXH_4TUPLE (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)
#define RXH_2TUPLE (RXH_IP_SRC | RXH_IP_DST)
static int bnxt_set_rxfh_fields(struct net_device *dev,
const struct ethtool_rxfh_fields *cmd,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
int tuple, rc = 0;
u32 rss_hash_cfg;
rss_hash_cfg = bp->rss_hash_cfg;
if (cmd->data == RXH_4TUPLE)
tuple = 4;
else if (cmd->data == RXH_2TUPLE)
tuple = 2;
else if (!cmd->data)
tuple = 0;
else
return -EINVAL;
if (cmd->flow_type == TCP_V4_FLOW) {
rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4;
if (tuple == 4)
rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV4;
} else if (cmd->flow_type == UDP_V4_FLOW) {
if (tuple == 4 && !(bp->rss_cap & BNXT_RSS_CAP_UDP_RSS_CAP))
return -EINVAL;
rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4;
if (tuple == 4)
rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV4;
} else if (cmd->flow_type == TCP_V6_FLOW) {
rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6;
if (tuple == 4)
rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_TCP_IPV6;
} else if (cmd->flow_type == UDP_V6_FLOW) {
if (tuple == 4 && !(bp->rss_cap & BNXT_RSS_CAP_UDP_RSS_CAP))
return -EINVAL;
rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6;
if (tuple == 4)
rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6;
} else if (cmd->flow_type == AH_ESP_V4_FLOW) {
if (tuple == 4 && (!(bp->rss_cap & BNXT_RSS_CAP_AH_V4_RSS_CAP) ||
!(bp->rss_cap & BNXT_RSS_CAP_ESP_V4_RSS_CAP)))
return -EINVAL;
rss_hash_cfg &= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |
VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4);
if (tuple == 4)
rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV4 |
VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV4;
} else if (cmd->flow_type == AH_ESP_V6_FLOW) {
if (tuple == 4 && (!(bp->rss_cap & BNXT_RSS_CAP_AH_V6_RSS_CAP) ||
!(bp->rss_cap & BNXT_RSS_CAP_ESP_V6_RSS_CAP)))
return -EINVAL;
rss_hash_cfg &= ~(VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |
VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6);
if (tuple == 4)
rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_AH_SPI_IPV6 |
VNIC_RSS_CFG_REQ_HASH_TYPE_ESP_SPI_IPV6;
} else if (tuple == 4) {
return -EINVAL;
}
switch (cmd->flow_type) {
case TCP_V4_FLOW:
case UDP_V4_FLOW:
case SCTP_V4_FLOW:
case AH_ESP_V4_FLOW:
case AH_V4_FLOW:
case ESP_V4_FLOW:
case IPV4_FLOW:
if (tuple == 2)
rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4;
else if (!tuple)
rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_IPV4;
break ;
case TCP_V6_FLOW:
case UDP_V6_FLOW:
case SCTP_V6_FLOW:
case AH_ESP_V6_FLOW:
case AH_V6_FLOW:
case ESP_V6_FLOW:
case IPV6_FLOW:
if (tuple == 2)
rss_hash_cfg |= VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6;
else if (!tuple)
rss_hash_cfg &= ~VNIC_RSS_CFG_REQ_HASH_TYPE_IPV6;
break ;
}
if (bp->rss_hash_cfg == rss_hash_cfg)
return 0;
if (bp->rss_cap & BNXT_RSS_CAP_RSS_HASH_TYPE_DELTA)
bp->rss_hash_delta = bp->rss_hash_cfg ^ rss_hash_cfg;
bp->rss_hash_cfg = rss_hash_cfg;
if (netif_running(bp->dev)) {
bnxt_close_nic(bp, false , false );
rc = bnxt_open_nic(bp, false , false );
}
return rc;
}
static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
struct bnxt *bp = netdev_priv(dev);
int rc = 0;
switch (cmd->cmd) {
case ETHTOOL_GRXRINGS:
cmd->data = bp->rx_nr_rings;
break ;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = bp->ntp_fltr_count;
cmd->data = bp->max_fltr | RX_CLS_LOC_SPECIAL;
break ;
case ETHTOOL_GRXCLSRLALL:
rc = bnxt_grxclsrlall(bp, cmd, (u32 *)rule_locs);
break ;
case ETHTOOL_GRXCLSRULE:
rc = bnxt_grxclsrule(bp, cmd);
break ;
default :
rc = -EOPNOTSUPP;
break ;
}
return rc;
}
static int bnxt_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
{
struct bnxt *bp = netdev_priv(dev);
int rc;
switch (cmd->cmd) {
case ETHTOOL_SRXCLSRLINS:
rc = bnxt_srxclsrlins(bp, cmd);
break ;
case ETHTOOL_SRXCLSRLDEL:
rc = bnxt_srxclsrldel(bp, cmd);
break ;
default :
rc = -EOPNOTSUPP;
break ;
}
return rc;
}
u32 bnxt_get_rxfh_indir_size(struct net_device *dev)
{
struct bnxt *bp = netdev_priv(dev);
if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS)
return bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) *
BNXT_RSS_TABLE_ENTRIES_P5;
return HW_HASH_INDEX_SIZE;
}
static u32 bnxt_get_rxfh_key_size(struct net_device *dev)
{
return HW_HASH_KEY_SIZE;
}
static int bnxt_get_rxfh(struct net_device *dev,
struct ethtool_rxfh_param *rxfh)
{
struct bnxt_rss_ctx *rss_ctx = NULL;
struct bnxt *bp = netdev_priv(dev);
u32 *indir_tbl = bp->rss_indir_tbl;
struct bnxt_vnic_info *vnic;
u32 i, tbl_size;
rxfh->hfunc = ETH_RSS_HASH_TOP;
if (!bp->vnic_info)
return 0;
vnic = &bp->vnic_info[BNXT_VNIC_DEFAULT];
if (rxfh->rss_context) {
struct ethtool_rxfh_context *ctx;
ctx = xa_load(&bp->dev->ethtool->rss_ctx, rxfh->rss_context);
if (!ctx)
return -EINVAL;
indir_tbl = ethtool_rxfh_context_indir(ctx);
rss_ctx = ethtool_rxfh_context_priv(ctx);
vnic = &rss_ctx->vnic;
}
if (rxfh->indir && indir_tbl) {
tbl_size = bnxt_get_rxfh_indir_size(dev);
for (i = 0; i < tbl_size; i++)
rxfh->indir[i] = indir_tbl[i];
}
if (rxfh->key && vnic->rss_hash_key)
memcpy(rxfh->key, vnic->rss_hash_key, HW_HASH_KEY_SIZE);
return 0;
}
static void bnxt_modify_rss(struct bnxt *bp, struct ethtool_rxfh_context *ctx,
struct bnxt_rss_ctx *rss_ctx,
const struct ethtool_rxfh_param *rxfh)
{
if (rxfh->key) {
if (rss_ctx) {
memcpy(rss_ctx->vnic.rss_hash_key, rxfh->key,
HW_HASH_KEY_SIZE);
} else {
memcpy(bp->rss_hash_key, rxfh->key, HW_HASH_KEY_SIZE);
bp->rss_hash_key_updated = true ;
}
}
if (rxfh->indir) {
u32 i, pad, tbl_size = bnxt_get_rxfh_indir_size(bp->dev);
u32 *indir_tbl = bp->rss_indir_tbl;
if (rss_ctx)
indir_tbl = ethtool_rxfh_context_indir(ctx);
for (i = 0; i < tbl_size; i++)
indir_tbl[i] = rxfh->indir[i];
pad = bp->rss_indir_tbl_entries - tbl_size;
if (pad)
memset(&indir_tbl[i], 0, pad * sizeof (*indir_tbl));
}
}
static int bnxt_rxfh_context_check(struct bnxt *bp,
const struct ethtool_rxfh_param *rxfh,
struct netlink_ext_ack *extack)
{
if (rxfh->hfunc && rxfh->hfunc != ETH_RSS_HASH_TOP) {
NL_SET_ERR_MSG_MOD(extack, "RSS hash function not supported" );
return -EOPNOTSUPP;
}
if (!BNXT_SUPPORTS_MULTI_RSS_CTX(bp)) {
NL_SET_ERR_MSG_MOD(extack, "RSS contexts not supported" );
return -EOPNOTSUPP;
}
if (!netif_running(bp->dev)) {
NL_SET_ERR_MSG_MOD(extack, "Unable to set RSS contexts when interface is down" );
return -EAGAIN;
}
return 0;
}
static int bnxt_create_rxfh_context(struct net_device *dev,
struct ethtool_rxfh_context *ctx,
const struct ethtool_rxfh_param *rxfh,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
struct bnxt_rss_ctx *rss_ctx;
struct bnxt_vnic_info *vnic;
int rc;
rc = bnxt_rxfh_context_check(bp, rxfh, extack);
if (rc)
return rc;
if (bp->num_rss_ctx >= BNXT_MAX_ETH_RSS_CTX) {
NL_SET_ERR_MSG_FMT_MOD(extack, "Out of RSS contexts, maximum %u" ,
BNXT_MAX_ETH_RSS_CTX);
return -EINVAL;
}
if (!bnxt_rfs_capable(bp, true )) {
NL_SET_ERR_MSG_MOD(extack, "Out hardware resources" );
return -ENOMEM;
}
rss_ctx = ethtool_rxfh_context_priv(ctx);
bp->num_rss_ctx++;
vnic = &rss_ctx->vnic;
vnic->rss_ctx = ctx;
vnic->flags |= BNXT_VNIC_RSSCTX_FLAG;
vnic->vnic_id = BNXT_VNIC_ID_INVALID;
rc = bnxt_alloc_vnic_rss_table(bp, vnic);
if (rc)
goto out;
/* Populate defaults in the context */
bnxt_set_dflt_rss_indir_tbl(bp, ctx);
ctx->hfunc = ETH_RSS_HASH_TOP;
memcpy(vnic->rss_hash_key, bp->rss_hash_key, HW_HASH_KEY_SIZE);
memcpy(ethtool_rxfh_context_key(ctx),
bp->rss_hash_key, HW_HASH_KEY_SIZE);
rc = bnxt_hwrm_vnic_alloc(bp, vnic, 0, bp->rx_nr_rings);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Unable to allocate VNIC" );
goto out;
}
rc = bnxt_hwrm_vnic_set_tpa(bp, vnic, bp->flags & BNXT_FLAG_TPA);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Unable to setup TPA" );
goto out;
}
bnxt_modify_rss(bp, ctx, rss_ctx, rxfh);
rc = __bnxt_setup_vnic_p5(bp, vnic);
if (rc) {
NL_SET_ERR_MSG_MOD(extack, "Unable to setup TPA" );
goto out;
}
rss_ctx->index = rxfh->rss_context;
return 0;
out:
bnxt_del_one_rss_ctx(bp, rss_ctx, true );
return rc;
}
static int bnxt_modify_rxfh_context(struct net_device *dev,
struct ethtool_rxfh_context *ctx,
const struct ethtool_rxfh_param *rxfh,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
struct bnxt_rss_ctx *rss_ctx;
int rc;
rc = bnxt_rxfh_context_check(bp, rxfh, extack);
if (rc)
return rc;
rss_ctx = ethtool_rxfh_context_priv(ctx);
bnxt_modify_rss(bp, ctx, rss_ctx, rxfh);
return bnxt_hwrm_vnic_rss_cfg_p5(bp, &rss_ctx->vnic);
}
static int bnxt_remove_rxfh_context(struct net_device *dev,
struct ethtool_rxfh_context *ctx,
u32 rss_context,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
struct bnxt_rss_ctx *rss_ctx;
rss_ctx = ethtool_rxfh_context_priv(ctx);
bnxt_del_one_rss_ctx(bp, rss_ctx, true );
return 0;
}
static int bnxt_set_rxfh(struct net_device *dev,
struct ethtool_rxfh_param *rxfh,
struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
int rc = 0;
if (rxfh->hfunc && rxfh->hfunc != ETH_RSS_HASH_TOP)
return -EOPNOTSUPP;
bnxt_modify_rss(bp, NULL, NULL, rxfh);
if (netif_running(bp->dev)) {
bnxt_close_nic(bp, false , false );
rc = bnxt_open_nic(bp, false , false );
}
return rc;
}
static void bnxt_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct bnxt *bp = netdev_priv(dev);
strscpy(info->driver, DRV_MODULE_NAME, sizeof (info->driver));
strscpy(info->fw_version, bp->fw_ver_str, sizeof (info->fw_version));
strscpy(info->bus_info, pci_name(bp->pdev), sizeof (info->bus_info));
info->n_stats = bnxt_get_num_stats(bp);
info->testinfo_len = bp->num_tests;
/* TODO CHIMP_FW: eeprom dump details */
info->eedump_len = 0;
/* TODO CHIMP FW: reg dump details */
info->regdump_len = 0;
}
static int bnxt_get_regs_len(struct net_device *dev)
{
struct bnxt *bp = netdev_priv(dev);
int reg_len;
if (!BNXT_PF(bp))
return -EOPNOTSUPP;
reg_len = BNXT_PXP_REG_LEN;
if (bp->fw_cap & BNXT_FW_CAP_PCIE_STATS_SUPPORTED)
reg_len += sizeof (struct pcie_ctx_hw_stats);
return reg_len;
}
#define BNXT_PCIE_32B_ENTRY(start, end) \
{ offsetof(struct pcie_ctx_hw_stats, start), \
offsetof(struct pcie_ctx_hw_stats, end) }
static const struct {
u16 start;
u16 end;
} bnxt_pcie_32b_entries[] = {
BNXT_PCIE_32B_ENTRY(pcie_ltssm_histogram[0], pcie_ltssm_histogram[3]),
};
static void bnxt_get_regs(struct net_device *dev, struct ethtool_regs *regs,
void *_p)
{
struct pcie_ctx_hw_stats *hw_pcie_stats;
struct hwrm_pcie_qstats_input *req;
struct bnxt *bp = netdev_priv(dev);
dma_addr_t hw_pcie_stats_addr;
int rc;
regs->version = 0;
if (!(bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_REG_ACCESS_RESTRICTED))
bnxt_dbg_hwrm_rd_reg(bp, 0, BNXT_PXP_REG_LEN / 4, _p);
if (!(bp->fw_cap & BNXT_FW_CAP_PCIE_STATS_SUPPORTED))
return ;
if (hwrm_req_init(bp, req, HWRM_PCIE_QSTATS))
return ;
hw_pcie_stats = hwrm_req_dma_slice(bp, req, sizeof (*hw_pcie_stats),
&hw_pcie_stats_addr);
if (!hw_pcie_stats) {
hwrm_req_drop(bp, req);
return ;
}
regs->version = 1;
hwrm_req_hold(bp, req); /* hold on to slice */
req->pcie_stat_size = cpu_to_le16(sizeof (*hw_pcie_stats));
req->pcie_stat_host_addr = cpu_to_le64(hw_pcie_stats_addr);
rc = hwrm_req_send(bp, req);
if (!rc) {
u8 *dst = (u8 *)(_p + BNXT_PXP_REG_LEN);
u8 *src = (u8 *)hw_pcie_stats;
int i, j;
for (i = 0, j = 0; i < sizeof (*hw_pcie_stats); ) {
if (i >= bnxt_pcie_32b_entries[j].start &&
i <= bnxt_pcie_32b_entries[j].end) {
u32 *dst32 = (u32 *)(dst + i);
*dst32 = le32_to_cpu(*(__le32 *)(src + i));
i += 4;
if (i > bnxt_pcie_32b_entries[j].end &&
j < ARRAY_SIZE(bnxt_pcie_32b_entries) - 1)
j++;
} else {
u64 *dst64 = (u64 *)(dst + i);
*dst64 = le64_to_cpu(*(__le64 *)(src + i));
i += 8;
}
}
}
hwrm_req_drop(bp, req);
}
static void bnxt_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bnxt *bp = netdev_priv(dev);
wol->supported = 0;
wol->wolopts = 0;
memset(&wol->sopass, 0, sizeof (wol->sopass));
if (bp->flags & BNXT_FLAG_WOL_CAP) {
wol->supported = WAKE_MAGIC;
if (bp->wol)
wol->wolopts = WAKE_MAGIC;
}
}
static int bnxt_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bnxt *bp = netdev_priv(dev);
if (wol->wolopts & ~WAKE_MAGIC)
return -EINVAL;
if (wol->wolopts & WAKE_MAGIC) {
if (!(bp->flags & BNXT_FLAG_WOL_CAP))
return -EINVAL;
if (!bp->wol) {
if (bnxt_hwrm_alloc_wol_fltr(bp))
return -EBUSY;
bp->wol = 1;
}
} else {
if (bp->wol) {
if (bnxt_hwrm_free_wol_fltr(bp))
return -EBUSY;
bp->wol = 0;
}
}
return 0;
}
/* TODO: support 25GB, 40GB, 50GB with different cable type */
void _bnxt_fw_to_linkmode(unsigned long *mode, u16 fw_speeds)
{
linkmode_zero(mode);
if (fw_speeds & BNXT_LINK_SPEED_MSK_100MB)
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode);
if (fw_speeds & BNXT_LINK_SPEED_MSK_1GB)
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode);
if (fw_speeds & BNXT_LINK_SPEED_MSK_2_5GB)
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, mode);
if (fw_speeds & BNXT_LINK_SPEED_MSK_10GB)
linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode);
if (fw_speeds & BNXT_LINK_SPEED_MSK_40GB)
linkmode_set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, mode);
}
enum bnxt_media_type {
BNXT_MEDIA_UNKNOWN = 0,
BNXT_MEDIA_TP,
BNXT_MEDIA_CR,
BNXT_MEDIA_SR,
BNXT_MEDIA_LR_ER_FR,
BNXT_MEDIA_KR,
BNXT_MEDIA_KX,
BNXT_MEDIA_X,
__BNXT_MEDIA_END,
};
static const enum bnxt_media_type bnxt_phy_types[] = {
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASECR] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASEKR4] = BNXT_MEDIA_KR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASELR] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASESR] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASEKR2] = BNXT_MEDIA_KR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASEKX] = BNXT_MEDIA_KX,
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASEKR] = BNXT_MEDIA_KR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASET] = BNXT_MEDIA_TP,
[PORT_PHY_QCFG_RESP_PHY_TYPE_BASETE] = BNXT_MEDIA_TP,
[PORT_PHY_QCFG_RESP_PHY_TYPE_25G_BASECR_CA_L] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_25G_BASECR_CA_S] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_25G_BASECR_CA_N] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_25G_BASESR] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASECR4] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASESR4] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASELR4] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASEER4] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASESR10] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_40G_BASECR4] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_40G_BASESR4] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_40G_BASELR4] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_40G_BASEER4] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_40G_ACTIVE_CABLE] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASET] = BNXT_MEDIA_TP,
[PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASESX] = BNXT_MEDIA_X,
[PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASECX] = BNXT_MEDIA_X,
[PORT_PHY_QCFG_RESP_PHY_TYPE_200G_BASECR4] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_200G_BASESR4] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_200G_BASELR4] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_200G_BASEER4] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_50G_BASECR] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_50G_BASESR] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_50G_BASELR] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_50G_BASEER] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASECR2] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASESR2] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASELR2] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASEER2] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASECR] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASESR] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASELR] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_100G_BASEER] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_200G_BASECR2] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_200G_BASESR2] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_200G_BASELR2] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_200G_BASEER2] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_400G_BASECR8] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_400G_BASESR8] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_400G_BASELR8] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_400G_BASEER8] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_400G_BASECR4] = BNXT_MEDIA_CR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_400G_BASESR4] = BNXT_MEDIA_SR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_400G_BASELR4] = BNXT_MEDIA_LR_ER_FR,
[PORT_PHY_QCFG_RESP_PHY_TYPE_400G_BASEER4] = BNXT_MEDIA_LR_ER_FR,
};
static enum bnxt_media_type
bnxt_get_media(struct bnxt_link_info *link_info)
{
switch (link_info->media_type) {
case PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP:
return BNXT_MEDIA_TP;
case PORT_PHY_QCFG_RESP_MEDIA_TYPE_DAC:
return BNXT_MEDIA_CR;
default :
if (link_info->phy_type < ARRAY_SIZE(bnxt_phy_types))
return bnxt_phy_types[link_info->phy_type];
return BNXT_MEDIA_UNKNOWN;
}
}
enum bnxt_link_speed_indices {
BNXT_LINK_SPEED_UNKNOWN = 0,
BNXT_LINK_SPEED_100MB_IDX,
BNXT_LINK_SPEED_1GB_IDX,
BNXT_LINK_SPEED_10GB_IDX,
BNXT_LINK_SPEED_25GB_IDX,
BNXT_LINK_SPEED_40GB_IDX,
BNXT_LINK_SPEED_50GB_IDX,
BNXT_LINK_SPEED_100GB_IDX,
BNXT_LINK_SPEED_200GB_IDX,
BNXT_LINK_SPEED_400GB_IDX,
__BNXT_LINK_SPEED_END
};
static enum bnxt_link_speed_indices bnxt_fw_speed_idx(u16 speed)
{
switch (speed) {
case BNXT_LINK_SPEED_100MB: return BNXT_LINK_SPEED_100MB_IDX;
case BNXT_LINK_SPEED_1GB: return BNXT_LINK_SPEED_1GB_IDX;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=100 H=90 G=95
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland