diff options
Diffstat (limited to 'drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c')
-rw-r--r-- | drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 1017 |
1 files changed, 749 insertions, 268 deletions
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index dc4ca706b0e2..9c5820839514 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -24,6 +24,7 @@ #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 "bnxt_hsi.h" #include "bnxt.h" @@ -631,13 +632,13 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, buf[j] = sw_stats[k]; skip_tpa_ring_stats: - sw = (u64 *)&cpr->sw_stats.rx; + 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; + sw = (u64 *)&cpr->sw_stats->cmn; for (k = 0; k < NUM_RING_CMN_SW_STATS; j++, k++) buf[j] = sw[k]; } @@ -705,112 +706,105 @@ skip_ring_stats: static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf) { struct bnxt *bp = netdev_priv(dev); - static const char * const *str; 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)) { - num_str = NUM_RING_RX_HW_STATS; - for (j = 0; j < num_str; j++) { - sprintf(buf, "[%d]: %s", i, - bnxt_ring_rx_stats_str[j]); - buf += ETH_GSTRING_LEN; + 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)) { - num_str = NUM_RING_TX_HW_STATS; - for (j = 0; j < num_str; j++) { - sprintf(buf, "[%d]: %s", i, - bnxt_ring_tx_stats_str[j]); - buf += ETH_GSTRING_LEN; + 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) - str = bnxt_ring_tpa2_stats_str; + for (j = 0; j < num_str; j++) { + str = bnxt_ring_tpa2_stats_str[j]; + ethtool_sprintf(&buf, "[%d]: %s", i, + str); + } else - str = bnxt_ring_tpa_stats_str; - - for (j = 0; j < num_str; j++) { - sprintf(buf, "[%d]: %s", i, str[j]); - buf += ETH_GSTRING_LEN; - } -skip_tpa_stats: - if (is_rx_ring(bp, i)) { - num_str = NUM_RING_RX_SW_STATS; for (j = 0; j < num_str; j++) { - sprintf(buf, "[%d]: %s", i, - bnxt_rx_sw_stats_str[j]); - buf += ETH_GSTRING_LEN; + 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); } - num_str = NUM_RING_CMN_SW_STATS; - for (j = 0; j < num_str; j++) { - sprintf(buf, "[%d]: %s", i, - bnxt_cmn_sw_stats_str[j]); - buf += ETH_GSTRING_LEN; - } - } - for (i = 0; i < BNXT_NUM_RING_ERR_STATS; i++) { - strscpy(buf, bnxt_ring_err_stats_arr[i], ETH_GSTRING_LEN); - buf += ETH_GSTRING_LEN; } + 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) { + if (bp->flags & BNXT_FLAG_PORT_STATS) for (i = 0; i < BNXT_NUM_PORT_STATS; i++) { - strcpy(buf, bnxt_port_stats_arr[i].string); - buf += ETH_GSTRING_LEN; + 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++) { - strcpy(buf, bnxt_port_stats_ext_arr[i].string); - buf += ETH_GSTRING_LEN; + 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++) { - strcpy(buf, - bnxt_tx_port_stats_ext_arr[i].string); - buf += ETH_GSTRING_LEN; + str = bnxt_tx_port_stats_ext_arr[i].string; + ethtool_puts(&buf, str); } + if (bp->pri2cos_valid) { for (i = 0; i < 8; i++) { - strcpy(buf, - bnxt_rx_bytes_pri_arr[i].string); - buf += ETH_GSTRING_LEN; + str = bnxt_rx_bytes_pri_arr[i].string; + ethtool_puts(&buf, str); } + for (i = 0; i < 8; i++) { - strcpy(buf, - bnxt_rx_pkts_pri_arr[i].string); - buf += ETH_GSTRING_LEN; + str = bnxt_rx_pkts_pri_arr[i].string; + ethtool_puts(&buf, str); } + for (i = 0; i < 8; i++) { - strcpy(buf, - bnxt_tx_bytes_pri_arr[i].string); - buf += ETH_GSTRING_LEN; + str = bnxt_tx_bytes_pri_arr[i].string; + ethtool_puts(&buf, str); } + for (i = 0; i < 8; i++) { - strcpy(buf, - bnxt_tx_pkts_pri_arr[i].string); - buf += ETH_GSTRING_LEN; + str = bnxt_tx_pkts_pri_arr[i].string; + ethtool_puts(&buf, str); } } } break; case ETH_SS_TEST: if (bp->num_tests) - memcpy(buf, bp->test_info->string, - bp->num_tests * ETH_GSTRING_LEN); + 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", @@ -840,6 +834,8 @@ static void bnxt_get_ringparam(struct net_device *dev, 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, @@ -847,16 +843,35 @@ static int bnxt_set_ringparam(struct net_device *dev, 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); @@ -955,11 +970,6 @@ static int bnxt_set_channels(struct net_device *dev, } tx_xdp = req_rx_rings; } - 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 (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) != bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) && @@ -968,6 +978,12 @@ static int bnxt_set_channels(struct net_device *dev, 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 @@ -1058,11 +1074,17 @@ static struct bnxt_filter_base *bnxt_get_one_fltr_rcu(struct bnxt *bp, 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, 0, cmd->rule_cnt); + rule_locs, count, + cmd->rule_cnt); rcu_read_unlock(); return 0; @@ -1074,13 +1096,44 @@ static int bnxt_grxclsrule(struct bnxt *bp, struct ethtool_rxnfc *cmd) (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 >= BNXT_NTP_FLTR_MAX_FLTR) + 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); @@ -1091,59 +1144,79 @@ static int bnxt_grxclsrule(struct bnxt *bp, struct ethtool_rxnfc *cmd) 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 == IPPROTO_TCP) + 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) + } else if (fkeys->basic.ip_proto == IPPROTO_UDP) { fs->flow_type = UDP_V4_FLOW; - else + } else { goto fltr_err; - - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_IP) { - fs->h_u.tcp_ip4_spec.ip4src = fkeys->addrs.v4addrs.src; - fs->m_u.tcp_ip4_spec.ip4src = cpu_to_be32(~0); } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_IP) { - fs->h_u.tcp_ip4_spec.ip4dst = fkeys->addrs.v4addrs.dst; - fs->m_u.tcp_ip4_spec.ip4dst = cpu_to_be32(~0); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_PORT) { + + 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 = cpu_to_be16(~0); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_PORT) { + 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 = cpu_to_be16(~0); + fs->m_u.tcp_ip4_spec.pdst = fmasks->ports.dst; } } else { - if (fkeys->basic.ip_proto == IPPROTO_TCP) + 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) + } else if (fkeys->basic.ip_proto == IPPROTO_UDP) { fs->flow_type = UDP_V6_FLOW; - else + } else { goto fltr_err; - - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_IP) { - *(struct in6_addr *)&fs->h_u.tcp_ip6_spec.ip6src[0] = - fkeys->addrs.v6addrs.src; - bnxt_fill_ipv6_mask(fs->m_u.tcp_ip6_spec.ip6src); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_IP) { - *(struct in6_addr *)&fs->h_u.tcp_ip6_spec.ip6dst[0] = - fkeys->addrs.v6addrs.dst; - bnxt_fill_ipv6_mask(fs->m_u.tcp_ip6_spec.ip6dst); } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_SRC_PORT) { + + *(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 = cpu_to_be16(~0); - } - if (fltr->ntuple_flags & BNXT_NTUPLE_MATCH_DST_PORT) { + 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 = cpu_to_be16(~0); + fs->m_u.tcp_ip6_spec.pdst = fmasks->ports.dst; } } - fs->ring_cookie = fltr->base.rxq; + 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: @@ -1152,48 +1225,170 @@ fltr_err: return rc; } -#define IPV4_ALL_MASK ((__force __be32)~0) -#define L4_PORT_ALL_MASK ((__force __be16)~0) +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 ipv6_mask_is_full(__be32 mask[4]) +static bool bnxt_verify_ntuple_ip4_flow(struct ethtool_usrip4_spec *ip_spec, + struct ethtool_usrip4_spec *ip_mask) { - return (mask[0] & mask[1] & mask[2] & mask[3]) == IPV4_ALL_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 ipv6_mask_is_zero(__be32 mask[4]) +static bool bnxt_verify_ntuple_ip6_flow(struct ethtool_usrip6_spec *ip_spec, + struct ethtool_usrip6_spec *ip_mask) { - return !(mask[0] | mask[1] | mask[2] | mask[3]); + 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_rx_flow_spec *fs) + struct ethtool_rxnfc *cmd) { - u8 vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie); - u32 ring = ethtool_get_flow_spec_ring(fs->ring_cookie); + 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; - u32 flow_type = fs->flow_type; + struct bnxt_flow_masks *fmasks; struct flow_keys *fkeys; - u32 idx; + u32 idx, ring; int rc; + u8 vf; if (!bp->vnic_info) return -EAGAIN; - if ((flow_type & (FLOW_MAC_EXT | FLOW_EXT)) || vf) + 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[0].l2_filters[0]; + 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; @@ -1203,32 +1398,27 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, 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; - if (ip_mask->ip4src == IPV4_ALL_MASK) { - fkeys->addrs.v4addrs.src = ip_spec->ip4src; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_SRC_IP; - } else if (ip_mask->ip4src) { - goto ntuple_err; - } - if (ip_mask->ip4dst == IPV4_ALL_MASK) { - fkeys->addrs.v4addrs.dst = ip_spec->ip4dst; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_DST_IP; - } else if (ip_mask->ip4dst) { - goto ntuple_err; - } - - if (ip_mask->psrc == L4_PORT_ALL_MASK) { - fkeys->ports.src = ip_spec->psrc; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_SRC_PORT; - } else if (ip_mask->psrc) { - goto ntuple_err; - } - if (ip_mask->pdst == L4_PORT_ALL_MASK) { - fkeys->ports.dst = ip_spec->pdst; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_DST_PORT; - } else if (ip_mask->pdst) { - goto ntuple_err; - } + 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: @@ -1241,40 +1431,21 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, fkeys->basic.ip_proto = IPPROTO_UDP; fkeys->basic.n_proto = htons(ETH_P_IPV6); - if (ipv6_mask_is_full(ip_mask->ip6src)) { - fkeys->addrs.v6addrs.src = - *(struct in6_addr *)&ip_spec->ip6src; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_SRC_IP; - } else if (!ipv6_mask_is_zero(ip_mask->ip6src)) { - goto ntuple_err; - } - if (ipv6_mask_is_full(ip_mask->ip6dst)) { - fkeys->addrs.v6addrs.dst = - *(struct in6_addr *)&ip_spec->ip6dst; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_DST_IP; - } else if (!ipv6_mask_is_zero(ip_mask->ip6dst)) { - goto ntuple_err; - } - - if (ip_mask->psrc == L4_PORT_ALL_MASK) { - fkeys->ports.src = ip_spec->psrc; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_SRC_PORT; - } else if (ip_mask->psrc) { - goto ntuple_err; - } - if (ip_mask->pdst == L4_PORT_ALL_MASK) { - fkeys->ports.dst = ip_spec->pdst; - new_fltr->ntuple_flags |= BNXT_NTUPLE_MATCH_DST_PORT; - } else if (ip_mask->pdst) { - goto ntuple_err; - } + 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 (!new_fltr->ntuple_flags) + if (!memcmp(&BNXT_FLOW_MASK_NONE, fmasks, sizeof(*fmasks))) goto ntuple_err; idx = bnxt_get_ntp_filter_idx(bp, fkeys, NULL); @@ -1287,8 +1458,24 @@ static int bnxt_add_ntuple_cls_rule(struct bnxt *bp, } rcu_read_unlock(); - new_fltr->base.rxq = ring; 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) { @@ -1321,6 +1508,18 @@ static int bnxt_srxclsrlins(struct bnxt *bp, struct ethtool_rxnfc *cmd) 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) @@ -1330,14 +1529,10 @@ static int bnxt_srxclsrlins(struct bnxt *bp, struct ethtool_rxnfc *cmd) if (!vf && ring >= bp->rx_nr_rings) return -EINVAL; - flow_type = fs->flow_type; - if (flow_type & (FLOW_MAC_EXT | FLOW_RSS)) - return -EINVAL; - flow_type &= ~FLOW_EXT; if (flow_type == ETHER_FLOW) - rc = -EOPNOTSUPP; + rc = bnxt_add_l2_cls_rule(bp, fs); else - rc = bnxt_add_ntuple_cls_rule(bp, fs); + rc = bnxt_add_ntuple_cls_rule(bp, cmd); return rc; } @@ -1346,11 +1541,22 @@ 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, - fs->location); + BNXT_NTP_FLTR_HASH_SIZE, id); if (!fltr_base) { rcu_read_unlock(); return -ENOENT; @@ -1396,8 +1602,14 @@ static int bnxt_grxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) 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_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: @@ -1415,8 +1627,14 @@ static int bnxt_grxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) 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_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: @@ -1463,6 +1681,24 @@ static int bnxt_srxfh(struct bnxt *bp, struct ethtool_rxnfc *cmd) 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; } @@ -1521,7 +1757,7 @@ static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, case ETHTOOL_GRXCLSRLCNT: cmd->rule_cnt = bp->ntp_fltr_count; - cmd->data = BNXT_NTP_FLTR_MAX_FLTR | RX_CLS_LOC_SPECIAL; + cmd->data = bp->max_fltr | RX_CLS_LOC_SPECIAL; break; case ETHTOOL_GRXCLSRLALL: @@ -1587,7 +1823,9 @@ static u32 bnxt_get_rxfh_key_size(struct net_device *dev) 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; @@ -1596,11 +1834,22 @@ static int bnxt_get_rxfh(struct net_device *dev, if (!bp->vnic_info) return 0; - vnic = &bp->vnic_info[0]; - if (rxfh->indir && bp->rss_indir_tbl) { + 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] = bp->rss_indir_tbl[i]; + rxfh->indir[i] = indir_tbl[i]; } if (rxfh->key && vnic->rss_hash_key) @@ -1609,6 +1858,159 @@ static int bnxt_get_rxfh(struct net_device *dev, 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) @@ -1619,18 +2021,7 @@ static int bnxt_set_rxfh(struct net_device *dev, if (rxfh->hfunc && rxfh->hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; - if (rxfh->key) - return -EOPNOTSUPP; - - if (rxfh->indir) { - u32 i, pad, tbl_size = bnxt_get_rxfh_indir_size(dev); - - for (i = 0; i < tbl_size; i++) - bp->rss_indir_tbl[i] = rxfh->indir[i]; - pad = bp->rss_indir_tbl_entries - tbl_size; - if (pad) - memset(&bp->rss_indir_tbl[i], 0, pad * sizeof(u16)); - } + bnxt_modify_rss(bp, NULL, NULL, rxfh); if (netif_running(bp->dev)) { bnxt_close_nic(bp, false, false); @@ -1681,7 +2072,8 @@ static void bnxt_get_regs(struct net_device *dev, struct ethtool_regs *regs, int rc; regs->version = 0; - bnxt_dbg_hwrm_rd_reg(bp, 0, BNXT_PXP_REG_LEN / 4, _p); + 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; @@ -1751,31 +2143,21 @@ static int bnxt_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) return 0; } -u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause) +/* TODO: support 25GB, 40GB, 50GB with different cable type */ +void _bnxt_fw_to_linkmode(unsigned long *mode, u16 fw_speeds) { - u32 speed_mask = 0; + linkmode_zero(mode); - /* TODO: support 25GB, 40GB, 50GB with different cable type */ - /* set the advertised speeds */ if (fw_speeds & BNXT_LINK_SPEED_MSK_100MB) - speed_mask |= ADVERTISED_100baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode); if (fw_speeds & BNXT_LINK_SPEED_MSK_1GB) - speed_mask |= ADVERTISED_1000baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode); if (fw_speeds & BNXT_LINK_SPEED_MSK_2_5GB) - speed_mask |= ADVERTISED_2500baseX_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, mode); if (fw_speeds & BNXT_LINK_SPEED_MSK_10GB) - speed_mask |= ADVERTISED_10000baseT_Full; + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode); if (fw_speeds & BNXT_LINK_SPEED_MSK_40GB) - speed_mask |= ADVERTISED_40000baseCR4_Full; - - if ((fw_pause & BNXT_LINK_PAUSE_BOTH) == BNXT_LINK_PAUSE_BOTH) - speed_mask |= ADVERTISED_Pause; - else if (fw_pause & BNXT_LINK_PAUSE_TX) - speed_mask |= ADVERTISED_Asym_Pause; - else if (fw_pause & BNXT_LINK_PAUSE_RX) - speed_mask |= ADVERTISED_Pause | ADVERTISED_Asym_Pause; - - return speed_mask; + linkmode_set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, mode); } enum bnxt_media_type { @@ -2482,19 +2864,24 @@ static int bnxt_get_link_ksettings(struct net_device *dev, } base->port = PORT_NONE; - if (link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP) { + if (media == BNXT_MEDIA_TP) { base->port = PORT_TP; linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, lk_ksettings->link_modes.supported); linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, lk_ksettings->link_modes.advertising); + } else if (media == BNXT_MEDIA_KR) { + linkmode_set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, + lk_ksettings->link_modes.supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, + lk_ksettings->link_modes.advertising); } else { linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, lk_ksettings->link_modes.supported); linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, lk_ksettings->link_modes.advertising); - if (link_info->media_type == PORT_PHY_QCFG_RESP_MEDIA_TYPE_DAC) + if (media == BNXT_MEDIA_CR) base->port = PORT_DA; else base->port = PORT_FIBRE; @@ -2643,23 +3030,22 @@ bnxt_force_link_speed(struct net_device *dev, u32 ethtool_speed, u32 lanes) return 0; } -u16 bnxt_get_fw_auto_link_speeds(u32 advertising) +u16 bnxt_get_fw_auto_link_speeds(const unsigned long *mode) { u16 fw_speed_mask = 0; - /* only support autoneg at speed 100, 1000, and 10000 */ - if (advertising & (ADVERTISED_100baseT_Full | - ADVERTISED_100baseT_Half)) { + if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mode) || + linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mode)) fw_speed_mask |= BNXT_LINK_SPEED_MSK_100MB; - } - if (advertising & (ADVERTISED_1000baseT_Full | - ADVERTISED_1000baseT_Half)) { + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mode) || + linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, mode)) fw_speed_mask |= BNXT_LINK_SPEED_MSK_1GB; - } - if (advertising & ADVERTISED_10000baseT_Full) + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, mode)) fw_speed_mask |= BNXT_LINK_SPEED_MSK_10GB; - if (advertising & ADVERTISED_40000baseCR4_Full) + if (linkmode_test_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, mode)) fw_speed_mask |= BNXT_LINK_SPEED_MSK_40GB; return fw_speed_mask; @@ -3798,12 +4184,12 @@ err: static void bnxt_get_pkgver(struct net_device *dev) { struct bnxt *bp = netdev_priv(dev); - char buf[FW_VER_STR_LEN]; + char buf[FW_VER_STR_LEN - 5]; int len; if (!bnxt_get_pkginfo(dev, buf, sizeof(buf))) { len = strlen(bp->fw_ver_str); - snprintf(bp->fw_ver_str + len, FW_VER_STR_LEN - len - 1, + snprintf(bp->fw_ver_str + len, FW_VER_STR_LEN - len, "/pkg %s", buf); } } @@ -3884,12 +4270,13 @@ static int bnxt_set_eeprom(struct net_device *dev, eeprom->len); } -static int bnxt_set_eee(struct net_device *dev, struct ethtool_eee *edata) +static int bnxt_set_eee(struct net_device *dev, struct ethtool_keee *edata) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp); struct bnxt *bp = netdev_priv(dev); - struct ethtool_eee *eee = &bp->eee; + struct ethtool_keee *eee = &bp->eee; struct bnxt_link_info *link_info = &bp->link_info; - u32 advertising; int rc = 0; if (!BNXT_PHY_CFG_ABLE(bp)) @@ -3899,7 +4286,7 @@ static int bnxt_set_eee(struct net_device *dev, struct ethtool_eee *edata) return -EOPNOTSUPP; mutex_lock(&bp->link_lock); - advertising = _bnxt_fw_to_ethtool_adv_spds(link_info->advertising, 0); + _bnxt_fw_to_linkmode(advertising, link_info->advertising); if (!edata->eee_enabled) goto eee_ok; @@ -3919,16 +4306,15 @@ static int bnxt_set_eee(struct net_device *dev, struct ethtool_eee *edata) edata->tx_lpi_timer = eee->tx_lpi_timer; } } - if (!edata->advertised) { - edata->advertised = advertising & eee->supported; - } else if (edata->advertised & ~advertising) { - netdev_warn(dev, "EEE advertised %x must be a subset of autoneg advertised speeds %x\n", - edata->advertised, advertising); + if (linkmode_empty(edata->advertised)) { + linkmode_and(edata->advertised, advertising, eee->supported); + } else if (linkmode_andnot(tmp, edata->advertised, advertising)) { + netdev_warn(dev, "EEE advertised must be a subset of autoneg advertised speeds\n"); rc = -EINVAL; goto eee_exit; } - eee->advertised = edata->advertised; + linkmode_copy(eee->advertised, edata->advertised); eee->tx_lpi_enabled = edata->tx_lpi_enabled; eee->tx_lpi_timer = edata->tx_lpi_timer; eee_ok: @@ -3942,7 +4328,7 @@ eee_exit: return rc; } -static int bnxt_get_eee(struct net_device *dev, struct ethtool_eee *edata) +static int bnxt_get_eee(struct net_device *dev, struct ethtool_keee *edata) { struct bnxt *bp = netdev_priv(dev); @@ -3954,12 +4340,51 @@ static int bnxt_get_eee(struct net_device *dev, struct ethtool_eee *edata) /* Preserve tx_lpi_timer so that the last value will be used * by default when it is re-enabled. */ - edata->advertised = 0; + linkmode_zero(edata->advertised); edata->tx_lpi_enabled = 0; } if (!bp->eee.eee_active) - edata->lp_advertised = 0; + linkmode_zero(edata->lp_advertised); + + return 0; +} + +static int bnxt_set_tunable(struct net_device *dev, + const struct ethtool_tunable *tuna, + const void *data) +{ + struct bnxt *bp = netdev_priv(dev); + u32 rx_copybreak; + + switch (tuna->id) { + case ETHTOOL_RX_COPYBREAK: + rx_copybreak = *(u32 *)data; + if (rx_copybreak > BNXT_MAX_RX_COPYBREAK) + return -ERANGE; + if (rx_copybreak != bp->rx_copybreak) { + if (netif_running(dev)) + return -EBUSY; + bp->rx_copybreak = rx_copybreak; + } + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int bnxt_get_tunable(struct net_device *dev, + const struct ethtool_tunable *tuna, void *data) +{ + struct bnxt *bp = netdev_priv(dev); + + switch (tuna->id) { + case ETHTOOL_RX_COPYBREAK: + *(u32 *)data = bp->rx_copybreak; + break; + default: + return -EOPNOTSUPP; + } return 0; } @@ -4012,6 +4437,9 @@ static int bnxt_get_module_info(struct net_device *dev, struct bnxt *bp = netdev_priv(dev); int rc; + if (BNXT_VF(bp) && !BNXT_VF_IS_TRUSTED(bp)) + return -EPERM; + /* No point in going further if phy status indicates * module is not inserted or if it is powered down or * if it is of type 10GBase-T @@ -4063,6 +4491,9 @@ static int bnxt_get_module_eeprom(struct net_device *dev, u16 start = eeprom->offset, length = eeprom->len; int rc = 0; + if (BNXT_VF(bp) && !BNXT_VF_IS_TRUSTED(bp)) + return -EPERM; + memset(data, 0, eeprom->len); /* Read A0 portion of the EEPROM */ @@ -4117,6 +4548,12 @@ static int bnxt_get_module_eeprom_by_page(struct net_device *dev, struct bnxt *bp = netdev_priv(dev); int rc; + if (BNXT_VF(bp) && !BNXT_VF_IS_TRUSTED(bp)) { + NL_SET_ERR_MSG_MOD(extack, + "Module read not permitted on untrusted VF"); + return -EPERM; + } + rc = bnxt_get_module_status(bp, extack); if (rc) return rc; @@ -4414,7 +4851,8 @@ static int bnxt_run_loopback(struct bnxt *bp) cpr = &rxr->bnapi->cp_ring; if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) cpr = rxr->rx_cpr; - pkt_size = min(bp->dev->mtu + ETH_HLEN, bp->rx_copy_thresh); + pkt_size = min(bp->dev->mtu + ETH_HLEN, max(BNXT_DEFAULT_RX_COPYBREAK, + bp->rx_copybreak)); skb = netdev_alloc_skb(bp->dev, pkt_size); if (!skb) return -ENOMEM; @@ -4483,6 +4921,14 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, if (!bp->num_tests || !BNXT_PF(bp)) return; + + if (etest->flags & ETH_TEST_FL_OFFLINE && + bnxt_ulp_registered(bp->edev)) { + etest->flags |= ETH_TEST_FL_FAILED; + netdev_warn(dev, "Offline tests cannot be run with RoCE driver loaded\n"); + return; + } + memset(buf, 0, sizeof(u64) * bp->num_tests); if (!netif_running(dev)) { etest->flags |= ETH_TEST_FL_FAILED; @@ -4513,45 +4959,51 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, if (!offline) { bnxt_run_fw_tests(bp, test_mask, &test_results); } else { - bnxt_ulp_stop(bp); bnxt_close_nic(bp, true, false); bnxt_run_fw_tests(bp, test_mask, &test_results); - buf[BNXT_MACLPBK_TEST_IDX] = 1; - bnxt_hwrm_mac_loopback(bp, true); - msleep(250); rc = bnxt_half_open_nic(bp); if (rc) { - bnxt_hwrm_mac_loopback(bp, false); etest->flags |= ETH_TEST_FL_FAILED; - bnxt_ulp_start(bp, rc); return; } + buf[BNXT_MACLPBK_TEST_IDX] = 1; + if (bp->mac_flags & BNXT_MAC_FL_NO_MAC_LPBK) + goto skip_mac_loopback; + + bnxt_hwrm_mac_loopback(bp, true); + msleep(250); if (bnxt_run_loopback(bp)) etest->flags |= ETH_TEST_FL_FAILED; else buf[BNXT_MACLPBK_TEST_IDX] = 0; bnxt_hwrm_mac_loopback(bp, false); +skip_mac_loopback: + buf[BNXT_PHYLPBK_TEST_IDX] = 1; + if (bp->phy_flags & BNXT_PHY_FL_NO_PHY_LPBK) + goto skip_phy_loopback; + bnxt_hwrm_phy_loopback(bp, true, false); msleep(1000); - if (bnxt_run_loopback(bp)) { - buf[BNXT_PHYLPBK_TEST_IDX] = 1; + if (bnxt_run_loopback(bp)) etest->flags |= ETH_TEST_FL_FAILED; - } + else + buf[BNXT_PHYLPBK_TEST_IDX] = 0; +skip_phy_loopback: + buf[BNXT_EXTLPBK_TEST_IDX] = 1; if (do_ext_lpbk) { etest->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE; bnxt_hwrm_phy_loopback(bp, true, true); msleep(1000); - if (bnxt_run_loopback(bp)) { - buf[BNXT_EXTLPBK_TEST_IDX] = 1; + if (bnxt_run_loopback(bp)) etest->flags |= ETH_TEST_FL_FAILED; - } + else + buf[BNXT_EXTLPBK_TEST_IDX] = 0; } bnxt_hwrm_phy_loopback(bp, false, false); bnxt_half_close_nic(bp); rc = bnxt_open_nic(bp, true, true); - bnxt_ulp_start(bp, rc); } if (rc || bnxt_test_irq(bp)) { buf[BNXT_IRQ_TEST_IDX] = 1; @@ -4625,14 +5077,21 @@ static int bnxt_set_dump(struct net_device *dev, struct ethtool_dump *dump) { struct bnxt *bp = netdev_priv(dev); - if (dump->flag > BNXT_DUMP_CRASH) { - netdev_info(dev, "Supports only Live(0) and Crash(1) dumps.\n"); + if (dump->flag > BNXT_DUMP_DRIVER) { + netdev_info(dev, "Supports only Live(0), Crash(1), Driver(2) dumps.\n"); return -EINVAL; } - if (!IS_ENABLED(CONFIG_TEE_BNXT_FW) && dump->flag == BNXT_DUMP_CRASH) { - netdev_info(dev, "Cannot collect crash dump as TEE_BNXT_FW config option is not enabled.\n"); - return -EOPNOTSUPP; + if (dump->flag == BNXT_DUMP_CRASH) { + if (bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_SOC_DDR && + (!IS_ENABLED(CONFIG_TEE_BNXT_FW))) { + netdev_info(dev, + "Cannot collect crash dump as TEE_BNXT_FW config option is not enabled.\n"); + return -EOPNOTSUPP; + } else if (!(bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_HOST_DDR)) { + netdev_info(dev, "Crash dump collection from host memory is not supported on this interface.\n"); + return -EOPNOTSUPP; + } } bp->dump_flag = dump->flag; @@ -4671,17 +5130,14 @@ static int bnxt_get_dump_data(struct net_device *dev, struct ethtool_dump *dump, } static int bnxt_get_ts_info(struct net_device *dev, - struct ethtool_ts_info *info) + struct kernel_ethtool_ts_info *info) { struct bnxt *bp = netdev_priv(dev); struct bnxt_ptp_cfg *ptp; ptp = bp->ptp_cfg; - info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE; + info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE; - info->phc_index = -1; if (!ptp) return 0; @@ -4891,6 +5347,19 @@ static void bnxt_get_rmon_stats(struct net_device *dev, *ranges = bnxt_rmon_ranges; } +static void bnxt_get_ptp_stats(struct net_device *dev, + struct ethtool_ts_stats *ts_stats) +{ + struct bnxt *bp = netdev_priv(dev); + struct bnxt_ptp_cfg *ptp = bp->ptp_cfg; + + if (ptp) { + ts_stats->pkts = ptp->stats.ts_pkts; + ts_stats->lost = ptp->stats.ts_lost; + ts_stats->err = atomic64_read(&ptp->stats.ts_err); + } +} + static void bnxt_get_link_ext_stats(struct net_device *dev, struct ethtool_link_ext_stats *stats) { @@ -4913,6 +5382,10 @@ void bnxt_ethtool_free(struct bnxt *bp) const struct ethtool_ops bnxt_ethtool_ops = { .cap_link_lanes_supported = 1, + .rxfh_per_ctx_key = 1, + .rxfh_max_num_contexts = BNXT_MAX_ETH_RSS_CTX + 1, + .rxfh_indir_space = BNXT_MAX_RSS_TABLE_ENTRIES_P5, + .rxfh_priv_size = sizeof(struct bnxt_rss_ctx), .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | ETHTOOL_COALESCE_USECS_IRQ | @@ -4920,6 +5393,8 @@ const struct ethtool_ops bnxt_ethtool_ops = { ETHTOOL_COALESCE_STATS_BLOCK_USECS | ETHTOOL_COALESCE_USE_ADAPTIVE_RX | ETHTOOL_COALESCE_USE_CQE, + .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT | + ETHTOOL_RING_USE_HDS_THRS, .get_link_ksettings = bnxt_get_link_ksettings, .set_link_ksettings = bnxt_set_link_ksettings, .get_fec_stats = bnxt_get_fec_stats, @@ -4950,6 +5425,9 @@ const struct ethtool_ops bnxt_ethtool_ops = { .get_rxfh_key_size = bnxt_get_rxfh_key_size, .get_rxfh = bnxt_get_rxfh, .set_rxfh = bnxt_set_rxfh, + .create_rxfh_context = bnxt_create_rxfh_context, + .modify_rxfh_context = bnxt_modify_rxfh_context, + .remove_rxfh_context = bnxt_remove_rxfh_context, .flash_device = bnxt_flash_device, .get_eeprom_len = bnxt_get_eeprom_len, .get_eeprom = bnxt_get_eeprom, @@ -4958,6 +5436,8 @@ const struct ethtool_ops bnxt_ethtool_ops = { .get_link_ext_stats = bnxt_get_link_ext_stats, .get_eee = bnxt_get_eee, .set_eee = bnxt_set_eee, + .get_tunable = bnxt_get_tunable, + .set_tunable = bnxt_set_tunable, .get_module_info = bnxt_get_module_info, .get_module_eeprom = bnxt_get_module_eeprom, .get_module_eeprom_by_page = bnxt_get_module_eeprom_by_page, @@ -4973,4 +5453,5 @@ const struct ethtool_ops bnxt_ethtool_ops = { .get_eth_mac_stats = bnxt_get_eth_mac_stats, .get_eth_ctrl_stats = bnxt_get_eth_ctrl_stats, .get_rmon_stats = bnxt_get_rmon_stats, + .get_ts_stats = bnxt_get_ptp_stats, }; |