diff options
Diffstat (limited to 'drivers/net/ethernet/broadcom/bnge/bnge_netdev.c')
| -rw-r--r-- | drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 1292 |
1 files changed, 1181 insertions, 111 deletions
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index 832eeb960bd2..a4288f0258f8 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -10,6 +10,9 @@ #include <linux/list.h> #include <linux/pci.h> #include <linux/netdevice.h> +#include <net/netdev_lock.h> +#include <net/netdev_queues.h> +#include <net/netdev_rx_queue.h> #include <linux/etherdevice.h> #include <linux/if.h> #include <net/ip.h> @@ -17,9 +20,11 @@ #include <net/page_pool/helpers.h> #include "bnge.h" +#include "bnge_hwrm.h" #include "bnge_hwrm_lib.h" #include "bnge_ethtool.h" #include "bnge_rmem.h" +#include "bnge_txrx.h" #define BNGE_RING_TO_TC_OFF(bd, tx) \ ((tx) % (bd)->tx_nr_rings_per_tc) @@ -35,6 +40,10 @@ static void bnge_free_stats_mem(struct bnge_net *bn, { struct bnge_dev *bd = bn->bd; + kfree(stats->hw_masks); + stats->hw_masks = NULL; + kfree(stats->sw_stats); + stats->sw_stats = NULL; if (stats->hw_stats) { dma_free_coherent(bd->dev, stats->len, stats->hw_stats, stats->hw_stats_map); @@ -43,7 +52,7 @@ static void bnge_free_stats_mem(struct bnge_net *bn, } static int bnge_alloc_stats_mem(struct bnge_net *bn, - struct bnge_stats_mem *stats) + struct bnge_stats_mem *stats, bool alloc_masks) { struct bnge_dev *bd = bn->bd; @@ -52,7 +61,23 @@ static int bnge_alloc_stats_mem(struct bnge_net *bn, if (!stats->hw_stats) return -ENOMEM; + stats->sw_stats = kzalloc(stats->len, GFP_KERNEL); + if (!stats->sw_stats) + goto stats_mem_err; + + if (alloc_masks) { + stats->hw_masks = kzalloc(stats->len, GFP_KERNEL); + if (!stats->hw_masks) + goto stats_mem_err; + } + + u64_stats_init(&stats->syncp); + return 0; + +stats_mem_err: + bnge_free_stats_mem(bn, stats); + return -ENOMEM; } static void bnge_free_ring_stats(struct bnge_net *bn) @@ -71,6 +96,107 @@ static void bnge_free_ring_stats(struct bnge_net *bn) } } +static void bnge_fill_masks(u64 *mask_arr, u64 mask, int count) +{ + int i; + + for (i = 0; i < count; i++) + mask_arr[i] = mask; +} + +void bnge_copy_hw_masks(u64 *mask_arr, __le64 *hw_mask_arr, int count) +{ + int i; + + for (i = 0; i < count; i++) + mask_arr[i] = le64_to_cpu(hw_mask_arr[i]); +} + +static void bnge_init_stats(struct bnge_net *bn) +{ + struct bnge_napi *bnapi = bn->bnapi[0]; + struct bnge_nq_ring_info *nqr; + struct bnge_stats_mem *stats; + struct bnge_dev *bd = bn->bd; + __le64 *rx_stats, *tx_stats; + int rc, rx_count, tx_count; + u64 *rx_masks, *tx_masks; + u8 flags; + + nqr = &bnapi->nq_ring; + stats = &nqr->stats; + rc = bnge_hwrm_func_qstat_ext(bd, stats); + if (rc) { + u64 mask = (1ULL << 48) - 1; + + bnge_fill_masks(stats->hw_masks, mask, stats->len / 8); + } + + if (bn->flags & BNGE_FLAG_PORT_STATS) { + stats = &bn->port_stats; + rx_stats = stats->hw_stats; + rx_masks = stats->hw_masks; + rx_count = sizeof(struct rx_port_stats) / 8; + tx_stats = rx_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8; + tx_masks = rx_masks + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8; + tx_count = sizeof(struct tx_port_stats) / 8; + + flags = PORT_QSTATS_REQ_FLAGS_COUNTER_MASK; + rc = bnge_hwrm_port_qstats(bd, flags); + if (rc) { + u64 mask = (1ULL << 40) - 1; + + bnge_fill_masks(rx_masks, mask, rx_count); + bnge_fill_masks(tx_masks, mask, tx_count); + } else { + bnge_copy_hw_masks(rx_masks, rx_stats, rx_count); + bnge_copy_hw_masks(tx_masks, tx_stats, tx_count); + } + bnge_hwrm_port_qstats(bd, 0); + } + + if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) { + stats = &bn->rx_port_stats_ext; + rx_stats = stats->hw_stats; + rx_masks = stats->hw_masks; + rx_count = sizeof(struct rx_port_stats_ext) / 8; + stats = &bn->tx_port_stats_ext; + tx_stats = stats->hw_stats; + tx_masks = stats->hw_masks; + tx_count = sizeof(struct tx_port_stats_ext) / 8; + + flags = PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK; + rc = bnge_hwrm_port_qstats_ext(bd, flags); + if (rc) { + u64 mask = (1ULL << 40) - 1; + + bnge_fill_masks(rx_masks, mask, rx_count); + if (tx_stats) + bnge_fill_masks(tx_masks, mask, tx_count); + } else { + bnge_copy_hw_masks(rx_masks, rx_stats, rx_count); + if (tx_stats) + bnge_copy_hw_masks(tx_masks, tx_stats, + tx_count); + } + bnge_hwrm_port_qstats_ext(bd, 0); + } +} + +static void bnge_free_port_ext_stats(struct bnge_net *bn) +{ + bn->flags &= ~BNGE_FLAG_PORT_STATS_EXT; + bnge_free_stats_mem(bn, &bn->rx_port_stats_ext); + bnge_free_stats_mem(bn, &bn->tx_port_stats_ext); +} + +static void bnge_free_port_stats(struct bnge_net *bn) +{ + bn->flags &= ~BNGE_FLAG_PORT_STATS; + bnge_free_stats_mem(bn, &bn->port_stats); + bnge_free_port_ext_stats(bn); +} + static int bnge_alloc_ring_stats(struct bnge_net *bn) { struct bnge_dev *bd = bn->bd; @@ -84,12 +210,13 @@ static int bnge_alloc_ring_stats(struct bnge_net *bn) struct bnge_nq_ring_info *nqr = &bnapi->nq_ring; nqr->stats.len = size; - rc = bnge_alloc_stats_mem(bn, &nqr->stats); + rc = bnge_alloc_stats_mem(bn, &nqr->stats, !i); if (rc) goto err_free_ring_stats; nqr->hw_stats_ctx_id = INVALID_STATS_CTX_ID; } + return 0; err_free_ring_stats: @@ -97,6 +224,227 @@ err_free_ring_stats: return rc; } +static void bnge_alloc_port_ext_stats(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int rc; + + if (!(bd->fw_cap & BNGE_FW_CAP_EXT_STATS_SUPPORTED)) + return; + + if (!bn->rx_port_stats_ext.hw_stats) { + bn->rx_port_stats_ext.len = sizeof(struct rx_port_stats_ext); + rc = bnge_alloc_stats_mem(bn, &bn->rx_port_stats_ext, true); + /* Extended stats are optional */ + if (rc) + return; + } + + if (!bn->tx_port_stats_ext.hw_stats) { + bn->tx_port_stats_ext.len = sizeof(struct tx_port_stats_ext); + rc = bnge_alloc_stats_mem(bn, &bn->tx_port_stats_ext, true); + /* Extended stats are optional */ + if (rc) { + bnge_free_port_ext_stats(bn); + return; + } + } + bn->flags |= BNGE_FLAG_PORT_STATS_EXT; +} + +static int bnge_alloc_port_stats(struct bnge_net *bn) +{ + int rc; + + if (!bn->port_stats.hw_stats) { + bn->port_stats.len = BNGE_PORT_STATS_SIZE; + rc = bnge_alloc_stats_mem(bn, &bn->port_stats, true); + if (rc) + return rc; + + bn->flags |= BNGE_FLAG_PORT_STATS; + } + + bnge_alloc_port_ext_stats(bn); + return 0; +} + +void __bnge_queue_sp_work(struct bnge_net *bn) +{ + queue_work(bn->bnge_pf_wq, &bn->sp_task); +} + +static void bnge_queue_sp_work(struct bnge_net *bn, unsigned int event) +{ + set_bit(event, &bn->sp_event); + __bnge_queue_sp_work(bn); +} + +static void bnge_timer(struct timer_list *t) +{ + struct bnge_net *bn = timer_container_of(bn, t, timer); + struct bnge_dev *bd = bn->bd; + + if (!netif_running(bn->netdev) || + !test_bit(BNGE_STATE_OPEN, &bd->state)) + return; + + if (READ_ONCE(bd->link_info.phy_retry)) { + if (time_after(jiffies, READ_ONCE(bd->link_info.phy_retry_expires))) { + WRITE_ONCE(bd->link_info.phy_retry, false); + netdev_warn(bn->netdev, "failed to update PHY settings after maximum retries.\n"); + } else { + bnge_queue_sp_work(bn, BNGE_UPDATE_PHY_SP_EVENT); + } + } + + if (BNGE_LINK_IS_UP(bd) && bn->stats_coal_ticks) + bnge_queue_sp_work(bn, BNGE_PERIODIC_STATS_SP_EVENT); + + mod_timer(&bn->timer, jiffies + bn->current_interval); +} + +static void bnge_add_one_ctr(u64 hw, u64 *sw, u64 mask) +{ + u64 sw_tmp, sw_val; + + hw &= mask; + sw_val = READ_ONCE(*sw); + sw_tmp = (sw_val & ~mask) | hw; + if (hw < (sw_val & mask)) + sw_tmp += mask + 1; + WRITE_ONCE(*sw, sw_tmp); +} + +static void __bnge_accumulate_stats(__le64 *hw_stats, u64 *sw_stats, u64 *masks, + int count, struct u64_stats_sync *syncp) +{ + unsigned long flags; + int i; + + flags = u64_stats_update_begin_irqsave(syncp); + for (i = 0; i < count; i++) { + u64 hw = le64_to_cpu(READ_ONCE(hw_stats[i])); + + if (masks[i] == -1ULL) + WRITE_ONCE(sw_stats[i], hw); + else + bnge_add_one_ctr(hw, &sw_stats[i], masks[i]); + } + u64_stats_update_end_irqrestore(syncp, flags); +} + +static void bnge_accumulate_stats(struct bnge_stats_mem *stats) +{ + if (!stats->hw_stats) + return; + + __bnge_accumulate_stats(stats->hw_stats, stats->sw_stats, + stats->hw_masks, stats->len / 8, &stats->syncp); +} + +static void bnge_accumulate_all_stats(struct bnge_dev *bd) +{ + struct bnge_net *bn = netdev_priv(bd->netdev); + struct bnge_stats_mem *ring0_stats = NULL; + int i; + + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + struct bnge_nq_ring_info *nqr; + struct bnge_stats_mem *stats; + + nqr = &bnapi->nq_ring; + stats = &nqr->stats; + + if (!ring0_stats) + ring0_stats = &bn->bnapi[0]->nq_ring.stats; + + __bnge_accumulate_stats(stats->hw_stats, stats->sw_stats, + ring0_stats->hw_masks, + ring0_stats->len / 8, &stats->syncp); + } + + if (bn->flags & BNGE_FLAG_PORT_STATS) { + struct bnge_stats_mem *stats = &bn->port_stats; + __le64 *hw_stats = stats->hw_stats; + u64 *sw_stats = stats->sw_stats; + u64 *masks = stats->hw_masks; + u16 cnt; + + cnt = sizeof(struct rx_port_stats) / 8; + __bnge_accumulate_stats(hw_stats, sw_stats, masks, cnt, + &stats->syncp); + + hw_stats += BNGE_TX_PORT_STATS_BYTE_OFFSET / 8; + sw_stats += BNGE_TX_PORT_STATS_BYTE_OFFSET / 8; + masks += BNGE_TX_PORT_STATS_BYTE_OFFSET / 8; + cnt = sizeof(struct tx_port_stats) / 8; + __bnge_accumulate_stats(hw_stats, sw_stats, masks, cnt, + &stats->syncp); + } + + if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) { + bnge_accumulate_stats(&bn->rx_port_stats_ext); + bnge_accumulate_stats(&bn->tx_port_stats_ext); + } +} + +static void bnge_sp_task(struct work_struct *work) +{ + struct bnge_net *bn = container_of(work, struct bnge_net, sp_task); + bool speed_chng, cfg_chng, link_chng; + struct bnge_dev *bd = bn->bd; + + netdev_lock(bn->netdev); + if (!test_bit(BNGE_STATE_OPEN, &bd->state)) { + netdev_unlock(bn->netdev); + return; + } + + if (test_and_clear_bit(BNGE_PERIODIC_STATS_SP_EVENT, &bn->sp_event)) { + bnge_hwrm_port_qstats(bd, 0); + bnge_hwrm_port_qstats_ext(bd, 0); + bnge_accumulate_all_stats(bd); + } + + if (test_and_clear_bit(BNGE_UPDATE_PHY_SP_EVENT, &bn->sp_event)) { + int rc; + + rc = bnge_update_phy_setting(bn); + if (rc) { + netdev_warn(bn->netdev, "update PHY settings retry failed\n"); + } else { + WRITE_ONCE(bd->link_info.phy_retry, false); + netdev_info(bn->netdev, "update PHY settings retry succeeded\n"); + } + } + + speed_chng = test_and_clear_bit(BNGE_LINK_SPEED_CHNG_SP_EVENT, + &bn->sp_event); + cfg_chng = test_and_clear_bit(BNGE_LINK_CFG_CHANGE_SP_EVENT, + &bn->sp_event); + link_chng = test_and_clear_bit(BNGE_LINK_CHNG_SP_EVENT, + &bn->sp_event); + + if (speed_chng || cfg_chng || link_chng) { + int rc; + + if (speed_chng) + bnge_hwrm_phy_qcaps(bd); + + rc = bnge_update_link(bn, true); + if (rc) + netdev_err(bn->netdev, "SP task cannot update link (rc: %d)\n", + rc); + + if (speed_chng || cfg_chng) + bnge_init_ethtool_link_settings(bn); + } + + netdev_unlock(bn->netdev); +} + static void bnge_free_nq_desc_arr(struct bnge_nq_ring_info *nqr) { struct bnge_ring_struct *ring = &nqr->ring_struct; @@ -123,11 +471,11 @@ static void bnge_free_cp_desc_arr(struct bnge_cp_ring_info *cpr) static int bnge_alloc_nq_desc_arr(struct bnge_nq_ring_info *nqr, int n) { - nqr->desc_ring = kcalloc(n, sizeof(*nqr->desc_ring), GFP_KERNEL); + nqr->desc_ring = kzalloc_objs(*nqr->desc_ring, n); if (!nqr->desc_ring) return -ENOMEM; - nqr->desc_mapping = kcalloc(n, sizeof(*nqr->desc_mapping), GFP_KERNEL); + nqr->desc_mapping = kzalloc_objs(*nqr->desc_mapping, n); if (!nqr->desc_mapping) goto err_free_desc_ring; return 0; @@ -140,11 +488,11 @@ err_free_desc_ring: static int bnge_alloc_cp_desc_arr(struct bnge_cp_ring_info *cpr, int n) { - cpr->desc_ring = kcalloc(n, sizeof(*cpr->desc_ring), GFP_KERNEL); + cpr->desc_ring = kzalloc_objs(*cpr->desc_ring, n); if (!cpr->desc_ring) return -ENOMEM; - cpr->desc_mapping = kcalloc(n, sizeof(*cpr->desc_mapping), GFP_KERNEL); + cpr->desc_mapping = kzalloc_objs(*cpr->desc_mapping, n); if (!cpr->desc_mapping) goto err_free_desc_ring; return 0; @@ -283,8 +631,7 @@ static int bnge_alloc_nq_tree(struct bnge_net *bn) tx = 1; } - nqr->cp_ring_arr = kcalloc(cp_count, sizeof(*cpr), - GFP_KERNEL); + nqr->cp_ring_arr = kzalloc_objs(*cpr, cp_count); if (!nqr->cp_ring_arr) { rc = -ENOMEM; goto err_free_nq_tree; @@ -372,11 +719,37 @@ static void bnge_free_one_agg_ring_bufs(struct bnge_net *bn, } } +static void bnge_free_one_tpa_info_data(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + int i; + + for (i = 0; i < bn->max_tpa; i++) { + struct bnge_tpa_info *tpa_info = &rxr->rx_tpa[i]; + u8 *data = tpa_info->data; + + if (!data) + continue; + + tpa_info->data = NULL; + page_pool_free_va(rxr->head_pool, data, false); + } +} + static void bnge_free_one_rx_ring_pair_bufs(struct bnge_net *bn, struct bnge_rx_ring_info *rxr) { + struct bnge_tpa_idx_map *map; + + if (rxr->rx_tpa) + bnge_free_one_tpa_info_data(bn, rxr); + bnge_free_one_rx_ring_bufs(bn, rxr); bnge_free_one_agg_ring_bufs(bn, rxr); + + map = rxr->rx_tpa_idx_map; + if (map) + memset(map->agg_idx_bmap, 0, sizeof(map->agg_idx_bmap)); } static void bnge_free_rx_ring_pair_bufs(struct bnge_net *bn) @@ -391,9 +764,116 @@ static void bnge_free_rx_ring_pair_bufs(struct bnge_net *bn) bnge_free_one_rx_ring_pair_bufs(bn, &bn->rx_ring[i]); } +static void bnge_free_tx_skbs(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + u16 max_idx; + int i; + + max_idx = bn->tx_nr_pages * TX_DESC_CNT; + for (i = 0; i < bd->tx_nr_rings; i++) { + struct bnge_tx_ring_info *txr = &bn->tx_ring[i]; + int j; + + if (!txr->tx_buf_ring) + continue; + + for (j = 0; j < max_idx;) { + struct bnge_sw_tx_bd *tx_buf = &txr->tx_buf_ring[j]; + struct sk_buff *skb; + int k, last; + + skb = tx_buf->skb; + if (!skb) { + j++; + continue; + } + + tx_buf->skb = NULL; + + dma_unmap_single(bd->dev, + dma_unmap_addr(tx_buf, mapping), + skb_headlen(skb), + DMA_TO_DEVICE); + + last = tx_buf->nr_frags; + j += 2; + for (k = 0; k < last; k++, j++) { + int ring_idx = j & bn->tx_ring_mask; + skb_frag_t *frag = &skb_shinfo(skb)->frags[k]; + + tx_buf = &txr->tx_buf_ring[ring_idx]; + dma_unmap_page(bd->dev, + dma_unmap_addr(tx_buf, mapping), + skb_frag_size(frag), + DMA_TO_DEVICE); + } + dev_kfree_skb(skb); + } + netdev_tx_reset_queue(netdev_get_tx_queue(bd->netdev, i)); + } +} + static void bnge_free_all_rings_bufs(struct bnge_net *bn) { bnge_free_rx_ring_pair_bufs(bn); + bnge_free_tx_skbs(bn); +} + +static void bnge_free_tpa_info(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i, j; + + for (i = 0; i < bd->rx_nr_rings; i++) { + struct bnge_rx_ring_info *rxr = &bn->rx_ring[i]; + + kfree(rxr->rx_tpa_idx_map); + rxr->rx_tpa_idx_map = NULL; + if (rxr->rx_tpa) { + for (j = 0; j < bn->max_tpa; j++) { + kfree(rxr->rx_tpa[j].agg_arr); + rxr->rx_tpa[j].agg_arr = NULL; + } + } + kfree(rxr->rx_tpa); + rxr->rx_tpa = NULL; + } +} + +static int bnge_alloc_tpa_info(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i, j; + + if (!bd->max_tpa_v2) + return 0; + + bn->max_tpa = max_t(u16, bd->max_tpa_v2, MAX_TPA); + for (i = 0; i < bd->rx_nr_rings; i++) { + struct bnge_rx_ring_info *rxr = &bn->rx_ring[i]; + + rxr->rx_tpa = kzalloc_objs(struct bnge_tpa_info, bn->max_tpa); + if (!rxr->rx_tpa) + goto err_free_tpa_info; + + for (j = 0; j < bn->max_tpa; j++) { + struct rx_agg_cmp *agg; + + agg = kzalloc_objs(*agg, MAX_SKB_FRAGS); + if (!agg) + goto err_free_tpa_info; + rxr->rx_tpa[j].agg_arr = agg; + } + rxr->rx_tpa_idx_map = kzalloc_obj(*rxr->rx_tpa_idx_map); + if (!rxr->rx_tpa_idx_map) + goto err_free_tpa_info; + } + return 0; + +err_free_tpa_info: + bnge_free_tpa_info(bn); + return -ENOMEM; } static void bnge_free_rx_rings(struct bnge_net *bn) @@ -401,6 +881,7 @@ static void bnge_free_rx_rings(struct bnge_net *bn) struct bnge_dev *bd = bn->bd; int i; + bnge_free_tpa_info(bn); for (i = 0; i < bd->rx_nr_rings; i++) { struct bnge_rx_ring_info *rxr = &bn->rx_ring[i]; struct bnge_ring_struct *ring; @@ -525,6 +1006,12 @@ static int bnge_alloc_rx_rings(struct bnge_net *bn) goto err_free_rx_rings; } } + + if (bn->priv_flags & BNGE_NET_EN_TPA) { + rc = bnge_alloc_tpa_info(bn); + if (rc) + goto err_free_rx_rings; + } return rc; err_free_rx_rings: @@ -667,8 +1154,7 @@ static int bnge_alloc_vnics(struct bnge_net *bn) */ num_vnics = 1; - bn->vnic_info = kcalloc(num_vnics, sizeof(struct bnge_vnic_info), - GFP_KERNEL); + bn->vnic_info = kzalloc_objs(struct bnge_vnic_info, num_vnics); if (!bn->vnic_info) return -ENOMEM; @@ -695,9 +1181,7 @@ static int bnge_init_ring_grps(struct bnge_net *bn) struct bnge_dev *bd = bn->bd; int i; - bn->grp_info = kcalloc(bd->nq_nr_rings, - sizeof(struct bnge_ring_grp_info), - GFP_KERNEL); + bn->grp_info = kzalloc_objs(struct bnge_ring_grp_info, bd->nq_nr_rings); if (!bn->grp_info) return -ENOMEM; for (i = 0; i < bd->nq_nr_rings; i++) { @@ -757,9 +1241,7 @@ static int bnge_alloc_core(struct bnge_net *bn) nqr->ring_struct.ring_mem.flags = BNGE_RMEM_RING_PTE_FLAG; } - bn->rx_ring = kcalloc(bd->rx_nr_rings, - sizeof(struct bnge_rx_ring_info), - GFP_KERNEL); + bn->rx_ring = kzalloc_objs(struct bnge_rx_ring_info, bd->rx_nr_rings); if (!bn->rx_ring) goto err_free_core; @@ -774,9 +1256,7 @@ static int bnge_alloc_core(struct bnge_net *bn) bn->bnapi[i]->rx_ring = &bn->rx_ring[i]; } - bn->tx_ring = kcalloc(bd->tx_nr_rings, - sizeof(struct bnge_tx_ring_info), - GFP_KERNEL); + bn->tx_ring = kzalloc_objs(struct bnge_tx_ring_info, bd->tx_nr_rings); if (!bn->tx_ring) goto err_free_core; @@ -811,6 +1291,8 @@ static int bnge_alloc_core(struct bnge_net *bn) if (rc) goto err_free_core; + bnge_init_stats(bn); + rc = bnge_alloc_vnics(bn); if (rc) goto err_free_core; @@ -846,16 +1328,23 @@ err_free_core: return rc; } -u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr) +u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr) { return rxr->rx_cpr->ring_struct.fw_ring_id; } -u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr) +u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr) { return txr->tx_cpr->ring_struct.fw_ring_id; } +static void bnge_db_nq_arm(struct bnge_net *bn, + struct bnge_db_info *db, u32 idx) +{ + bnge_writeq(bn->bd, db->db_key64 | DBR_TYPE_NQ_ARM | + DB_RING_IDX(db, idx), db->doorbell); +} + static void bnge_db_nq(struct bnge_net *bn, struct bnge_db_info *db, u32 idx) { bnge_writeq(bn->bd, db->db_key64 | DBR_TYPE_NQ_MASK | @@ -878,12 +1367,6 @@ static int bnge_cp_num_to_irq_num(struct bnge_net *bn, int n) return nqr->ring_struct.map_idx; } -static irqreturn_t bnge_msix(int irq, void *dev_instance) -{ - /* NAPI scheduling to be added in a future patch */ - return IRQ_HANDLED; -} - static void bnge_init_nq_tree(struct bnge_net *bn) { struct bnge_dev *bd = bn->bd; @@ -893,12 +1376,12 @@ static void bnge_init_nq_tree(struct bnge_net *bn) struct bnge_nq_ring_info *nqr = &bn->bnapi[i]->nq_ring; struct bnge_ring_struct *ring = &nqr->ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; for (j = 0; j < nqr->cp_ring_count; j++) { struct bnge_cp_ring_info *cpr = &nqr->cp_ring_arr[j]; ring = &cpr->ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } } } @@ -925,9 +1408,9 @@ static netmem_ref __bnge_alloc_rx_netmem(struct bnge_net *bn, return netmem; } -static u8 *__bnge_alloc_rx_frag(struct bnge_net *bn, dma_addr_t *mapping, - struct bnge_rx_ring_info *rxr, - gfp_t gfp) +u8 *__bnge_alloc_rx_frag(struct bnge_net *bn, dma_addr_t *mapping, + struct bnge_rx_ring_info *rxr, + gfp_t gfp) { unsigned int offset; struct page *page; @@ -941,9 +1424,8 @@ static u8 *__bnge_alloc_rx_frag(struct bnge_net *bn, dma_addr_t *mapping, return page_address(page) + offset; } -static int bnge_alloc_rx_data(struct bnge_net *bn, - struct bnge_rx_ring_info *rxr, - u16 prod, gfp_t gfp) +int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr, + u16 prod, gfp_t gfp) { struct bnge_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[RING_RX(bn, prod)]; struct rx_bd *rxbd; @@ -995,7 +1477,7 @@ static int bnge_alloc_one_rx_ring_bufs(struct bnge_net *bn, return 0; } -static u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx) +u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx) { u16 next, max = rxr->rx_agg_bmap_size; @@ -1005,9 +1487,9 @@ static u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx) return next; } -static int bnge_alloc_rx_netmem(struct bnge_net *bn, - struct bnge_rx_ring_info *rxr, - u16 prod, gfp_t gfp) +int bnge_alloc_rx_netmem(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr, + u16 prod, gfp_t gfp) { struct bnge_sw_rx_agg_bd *rx_agg_buf; u16 sw_prod = rxr->rx_sw_agg_prod; @@ -1070,6 +1552,30 @@ err_free_one_agg_ring_bufs: return -ENOMEM; } +static int bnge_alloc_one_tpa_info_data(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + dma_addr_t mapping; + u8 *data; + int i; + + for (i = 0; i < bn->max_tpa; i++) { + data = __bnge_alloc_rx_frag(bn, &mapping, rxr, + GFP_KERNEL); + if (!data) + goto err_free_tpa_info_data; + + rxr->rx_tpa[i].data = data; + rxr->rx_tpa[i].data_ptr = data + bn->rx_offset; + rxr->rx_tpa[i].mapping = mapping; + } + return 0; + +err_free_tpa_info_data: + bnge_free_one_tpa_info_data(bn, rxr); + return -ENOMEM; +} + static int bnge_alloc_one_rx_ring_pair_bufs(struct bnge_net *bn, int ring_nr) { struct bnge_rx_ring_info *rxr = &bn->rx_ring[ring_nr]; @@ -1084,8 +1590,17 @@ static int bnge_alloc_one_rx_ring_pair_bufs(struct bnge_net *bn, int ring_nr) if (rc) goto err_free_one_rx_ring_bufs; } + + if (rxr->rx_tpa) { + rc = bnge_alloc_one_tpa_info_data(bn, rxr); + if (rc) + goto err_free_one_agg_ring_bufs; + } + return 0; +err_free_one_agg_ring_bufs: + bnge_free_one_agg_ring_bufs(bn, rxr); err_free_one_rx_ring_bufs: bnge_free_one_rx_ring_bufs(bn, rxr); return rc; @@ -1123,7 +1638,7 @@ static void bnge_init_one_rx_ring_rxbd(struct bnge_net *bn, ring = &rxr->rx_ring_struct; bnge_init_rxbd_pages(ring, type); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn, @@ -1133,7 +1648,7 @@ static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn, u32 type; ring = &rxr->rx_agg_ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; if (bnge_is_agg_reqd(bn->bd)) { type = ((u32)BNGE_RX_PAGE_SIZE << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP; @@ -1194,7 +1709,7 @@ static void bnge_init_tx_rings(struct bnge_net *bn) struct bnge_tx_ring_info *txr = &bn->tx_ring[i]; struct bnge_ring_struct *ring = &txr->tx_ring_struct; - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; netif_queue_set_napi(bn->netdev, i, NETDEV_QUEUE_TYPE_TX, &txr->bnapi->napi); @@ -1353,7 +1868,7 @@ static int bnge_hwrm_rx_agg_ring_alloc(struct bnge_net *bn, ring->fw_ring_id); bnge_db_write(bn->bd, &rxr->rx_agg_db, rxr->rx_agg_prod); bnge_db_write(bn->bd, &rxr->rx_db, rxr->rx_prod); - bn->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id; + bn->grp_info[grp_idx].agg_fw_ring_id = (u16)ring->fw_ring_id; return 0; } @@ -1372,7 +1887,7 @@ static int bnge_hwrm_rx_ring_alloc(struct bnge_net *bn, return rc; bnge_set_db(bn, &rxr->rx_db, type, map_idx, ring->fw_ring_id); - bn->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id; + bn->grp_info[map_idx].rx_fw_ring_id = (u16)ring->fw_ring_id; return 0; } @@ -1402,7 +1917,7 @@ static int bnge_hwrm_ring_alloc(struct bnge_net *bn) bnge_set_db(bn, &nqr->nq_db, type, map_idx, ring->fw_ring_id); bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons); enable_irq(vector); - bn->grp_info[i].nq_fw_ring_id = ring->fw_ring_id; + bn->grp_info[i].nq_fw_ring_id = (u16)ring->fw_ring_id; if (!i) { rc = bnge_hwrm_set_async_event_cr(bd, ring->fw_ring_id); @@ -1472,15 +1987,13 @@ void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic) tbl_size = bnge_get_rxfh_indir_size(bd); for (i = 0; i < tbl_size; i++) { - u16 ring_id, j; + u32 j; j = bd->rss_indir_tbl[i]; rxr = &bn->rx_ring[j]; - ring_id = rxr->rx_ring_struct.fw_ring_id; - *ring_tbl++ = cpu_to_le16(ring_id); - ring_id = bnge_cp_ring_for_rx(rxr); - *ring_tbl++ = cpu_to_le16(ring_id); + *ring_tbl++ = cpu_to_le16(rxr->rx_ring_struct.fw_ring_id); + *ring_tbl++ = cpu_to_le16(bnge_cp_ring_for_rx(rxr)); } } @@ -1599,7 +2112,7 @@ static struct bnge_l2_filter *bnge_alloc_l2_filter(struct bnge_net *bn, if (fltr) return fltr; - fltr = kzalloc(sizeof(*fltr), gfp); + fltr = kzalloc_obj(*fltr, gfp); if (!fltr) return ERR_PTR(-ENOMEM); @@ -1632,16 +2145,16 @@ err_del_l2_filter: return rc; } -static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask) +static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask, + const struct netdev_hw_addr_list *mc) { struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; - struct net_device *dev = bn->netdev; struct netdev_hw_addr *ha; int mc_count = 0, off = 0; bool update = false; u8 *haddr; - netdev_for_each_mc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, mc) { if (mc_count >= BNGE_MAX_MC_ADDRS) { *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; vnic->mc_list_count = 0; @@ -1665,17 +2178,17 @@ static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask) return update; } -static bool bnge_uc_list_updated(struct bnge_net *bn) +static bool bnge_uc_list_updated(struct bnge_net *bn, + const struct netdev_hw_addr_list *uc) { struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; - struct net_device *dev = bn->netdev; struct netdev_hw_addr *ha; int off = 0; - if (netdev_uc_count(dev) != (vnic->uc_filter_count - 1)) + if (netdev_hw_addr_list_count(uc) != (vnic->uc_filter_count - 1)) return true; - netdev_for_each_uc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, uc) { if (!ether_addr_equal(ha->addr, vnic->uc_list + off)) return true; @@ -1689,18 +2202,14 @@ static bool bnge_promisc_ok(struct bnge_net *bn) return true; } -static int bnge_cfg_def_vnic(struct bnge_net *bn) +static int bnge_cfg_rx_mode(struct bnge_net *bn, struct netdev_hw_addr_list *uc, + bool uc_update, bool snapshot) { struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; struct net_device *dev = bn->netdev; struct bnge_dev *bd = bn->bd; struct netdev_hw_addr *ha; int i, off = 0, rc; - bool uc_update; - - netif_addr_lock_bh(dev); - uc_update = bnge_uc_list_updated(bn); - netif_addr_unlock_bh(dev); if (!uc_update) goto skip_uc; @@ -1714,22 +2223,28 @@ static int bnge_cfg_def_vnic(struct bnge_net *bn) vnic->uc_filter_count = 1; - netif_addr_lock_bh(dev); - if (netdev_uc_count(dev) > (BNGE_MAX_UC_ADDRS - 1)) { + if (!snapshot) + netif_addr_lock_bh(dev); + if (netdev_hw_addr_list_count(uc) > (BNGE_MAX_UC_ADDRS - 1)) { vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; } else { - netdev_for_each_uc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, uc) { memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN); off += ETH_ALEN; vnic->uc_filter_count++; } } - netif_addr_unlock_bh(dev); + if (!snapshot) + netif_addr_unlock_bh(dev); for (i = 1, off = 0; i < vnic->uc_filter_count; i++, off += ETH_ALEN) { rc = bnge_hwrm_set_vnic_filter(bn, 0, i, vnic->uc_list + off); if (rc) { - netdev_err(dev, "HWRM vnic filter failure rc: %d\n", rc); + if (rc == -EAGAIN) + netdev_warn(dev, "FW busy while setting vnic filter, will retry\n"); + else + netdev_err(dev, "HWRM vnic filter failure rc: %d\n", + rc); vnic->uc_filter_count = i; return rc; } @@ -1748,13 +2263,137 @@ skip_uc: vnic->mc_list_count = 0; rc = bnge_hwrm_cfa_l2_set_rx_mask(bd, vnic); } - if (rc) - netdev_err(dev, "HWRM cfa l2 rx mask failure rc: %d\n", - rc); + if (rc) { + if (rc == -EAGAIN) { + netdev_warn(dev, "FW busy while setting l2 rx mask in CFA, will retry\n"); + vnic->rx_mask &= ~BNGE_RX_MASK_CFG_FLAGS; + } else { + netdev_err(dev, "HWRM CFA L2 rx mask failure rc: %d\n", + rc); + } + } return rc; } +static int bnge_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) +{ + struct bnge_net *bn = netdev_priv(dev); + struct bnge_vnic_info *vnic; + bool mc_update = false; + bool uc_update; + u32 mask; + + if (!test_bit(BNGE_STATE_OPEN, &bn->bd->state)) + return 0; + + vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; + mask = vnic->rx_mask; + mask &= ~BNGE_RX_MASK_CFG_FLAGS; + + if (dev->flags & IFF_PROMISC) + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; + + uc_update = bnge_uc_list_updated(bn, uc); + + if (dev->flags & IFF_BROADCAST) + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; + if (dev->flags & IFF_ALLMULTI) { + mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; + vnic->mc_list_count = 0; + } else if (dev->flags & IFF_MULTICAST) { + mc_update = bnge_mc_list_updated(bn, &mask, mc); + } + + if (mask != vnic->rx_mask || uc_update || mc_update) { + vnic->rx_mask = mask; + return bnge_cfg_rx_mode(bn, uc, uc_update, true); + } + + return 0; +} + +static void bnge_disable_int(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + if (!bn->bnapi) + return; + + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + struct bnge_nq_ring_info *nqr; + struct bnge_ring_struct *ring; + + nqr = &bnapi->nq_ring; + ring = &nqr->ring_struct; + + if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT) + bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons); + } +} + +static void bnge_disable_int_sync(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + bnge_disable_int(bn); + for (i = 0; i < bd->nq_nr_rings; i++) { + int map_idx = bnge_cp_num_to_irq_num(bn, i); + + synchronize_irq(bd->irq_tbl[map_idx].vector); + } +} + +static void bnge_enable_int(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + struct bnge_nq_ring_info *nqr; + + nqr = &bnapi->nq_ring; + bnge_db_nq_arm(bn, &nqr->nq_db, nqr->nq_raw_cons); + } +} + +static void bnge_disable_napi(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + if (test_and_set_bit(BNGE_STATE_NAPI_DISABLED, &bn->state)) + return; + + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + + napi_disable_locked(&bnapi->napi); + } +} + +static void bnge_enable_napi(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + clear_bit(BNGE_STATE_NAPI_DISABLED, &bn->state); + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + + bnapi->in_reset = false; + bnapi->tx_fault = 0; + + napi_enable_locked(&bnapi->napi); + } +} + static void bnge_hwrm_vnic_free(struct bnge_net *bn) { int i; @@ -1808,7 +2447,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn, u32 grp_idx = rxr->bnapi->index; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = bnge_cp_ring_for_rx(rxr); @@ -1816,7 +2455,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn, RING_FREE_REQ_RING_TYPE_RX, close_path ? cmpl_ring_id : INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[grp_idx].rx_fw_ring_id = INVALID_HW_RING_ID; } @@ -1828,14 +2467,14 @@ static void bnge_hwrm_rx_agg_ring_free(struct bnge_net *bn, u32 grp_idx = rxr->bnapi->index; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = bnge_cp_ring_for_rx(rxr); hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_RX_AGG, close_path ? cmpl_ring_id : INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[grp_idx].agg_fw_ring_id = INVALID_HW_RING_ID; } @@ -1846,14 +2485,14 @@ static void bnge_hwrm_tx_ring_free(struct bnge_net *bn, struct bnge_ring_struct *ring = &txr->tx_ring_struct; u32 cmpl_ring_id; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; cmpl_ring_id = close_path ? bnge_cp_ring_for_tx(txr) : INVALID_HW_RING_ID; hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_TX, cmpl_ring_id); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_hwrm_cp_ring_free(struct bnge_net *bn, @@ -1862,12 +2501,12 @@ static void bnge_hwrm_cp_ring_free(struct bnge_net *bn, struct bnge_ring_struct *ring; ring = &cpr->ring_struct; - if (ring->fw_ring_id == INVALID_HW_RING_ID) + if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT) return; hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_L2_CMPL, INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; } static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path) @@ -1886,6 +2525,12 @@ static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path) bnge_hwrm_rx_agg_ring_free(bn, &bn->rx_ring[i], close_path); } + /* The completion rings are about to be freed. After that the + * IRQ doorbell will not work anymore. So we need to disable + * IRQ here. + */ + bnge_disable_int_sync(bn); + for (i = 0; i < bd->nq_nr_rings; i++) { struct bnge_napi *bnapi = bn->bnapi[i]; struct bnge_nq_ring_info *nqr; @@ -1897,11 +2542,11 @@ static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path) bnge_hwrm_cp_ring_free(bn, &nqr->cp_ring_arr[j]); ring = &nqr->ring_struct; - if (ring->fw_ring_id != INVALID_HW_RING_ID) { + if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT) { hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_NQ, INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; + ring->fw_ring_id = INVALID_HW_RING_ID_32BIT; bn->grp_info[i].nq_fw_ring_id = INVALID_HW_RING_ID; } } @@ -2015,6 +2660,27 @@ err_free_irq: return rc; } +static int bnge_set_tpa(struct bnge_net *bn, bool set_tpa) +{ + u32 tpa_flags = 0; + int rc, i; + + if (set_tpa) + tpa_flags = bn->priv_flags & BNGE_NET_EN_TPA; + else if (BNGE_NO_FW_ACCESS(bn->bd)) + return 0; + for (i = 0; i < bn->nr_vnics; i++) { + rc = bnge_hwrm_vnic_set_tpa(bn->bd, &bn->vnic_info[i], + tpa_flags); + if (rc) { + netdev_err(bn->netdev, "hwrm vnic set tpa failure rc for vnic %d: %x\n", + i, rc); + return rc; + } + } + return 0; +} + static int bnge_init_chip(struct bnge_net *bn) { struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; @@ -2049,6 +2715,12 @@ static int bnge_init_chip(struct bnge_net *bn) if (bd->rss_cap & BNGE_RSS_CAP_RSS_HASH_TYPE_DELTA) bnge_hwrm_update_rss_hash_cfg(bn); + if (bn->priv_flags & BNGE_NET_EN_TPA) { + rc = bnge_set_tpa(bn, true); + if (rc) + goto err_out; + } + /* Filter for default vnic 0 */ rc = bnge_hwrm_set_vnic_filter(bn, 0, 0, bn->netdev->dev_addr); if (rc) { @@ -2071,13 +2743,17 @@ static int bnge_init_chip(struct bnge_net *bn) } else if (bn->netdev->flags & IFF_MULTICAST) { u32 mask = 0; - bnge_mc_list_updated(bn, &mask); + bnge_mc_list_updated(bn, &mask, &bn->netdev->mc); vnic->rx_mask |= mask; } - rc = bnge_cfg_def_vnic(bn); - if (rc) + rc = bnge_cfg_rx_mode(bn, &bn->netdev->uc, true, false); + if (rc == -EAGAIN) { + netif_rx_mode_schedule_retry(bn->netdev); + rc = 0; + } else if (rc) { goto err_out; + } return 0; err_out: @@ -2085,16 +2761,6 @@ err_out: return rc; } -static int bnge_napi_poll(struct napi_struct *napi, int budget) -{ - int work_done = 0; - - /* defer NAPI implementation to next patch series */ - napi_complete_done(napi, work_done); - - return work_done; -} - static void bnge_init_napi(struct bnge_net *bn) { struct bnge_dev *bd = bn->bd; @@ -2154,13 +2820,67 @@ static int bnge_init_nic(struct bnge_net *bn) err_free_ring_grps: bnge_free_ring_grps(bn); - return rc; - err_free_rx_ring_pair_bufs: bnge_free_rx_ring_pair_bufs(bn); return rc; } +static void bnge_tx_disable(struct bnge_net *bn) +{ + struct bnge_tx_ring_info *txr; + int i; + + if (bn->tx_ring) { + for (i = 0; i < bn->bd->tx_nr_rings; i++) { + txr = &bn->tx_ring[i]; + WRITE_ONCE(txr->dev_state, BNGE_DEV_STATE_CLOSING); + } + } + /* Make sure napi polls see @dev_state change */ + synchronize_net(); + + if (!bn->netdev) + return; + /* Drop carrier first to prevent TX timeout */ + netif_carrier_off(bn->netdev); + /* Stop all TX queues */ + netif_tx_disable(bn->netdev); +} + +static void bnge_tx_enable(struct bnge_net *bn) +{ + struct bnge_tx_ring_info *txr; + int i; + + for (i = 0; i < bn->bd->tx_nr_rings; i++) { + txr = &bn->tx_ring[i]; + WRITE_ONCE(txr->dev_state, 0); + } + /* Make sure napi polls see @dev_state change */ + synchronize_net(); + netif_tx_wake_all_queues(bn->netdev); + if (BNGE_LINK_IS_UP(bn->bd)) + netif_carrier_on(bn->netdev); +} + +static int bnge_hwrm_if_change(struct bnge_dev *bd, bool up) +{ + struct hwrm_func_drv_if_change_input *req; + int rc; + + if (!(bd->fw_cap & BNGE_FW_CAP_IF_CHANGE)) + return 0; + + rc = bnge_hwrm_req_init(bd, req, HWRM_FUNC_DRV_IF_CHANGE); + if (rc) + return rc; + + if (up) + req->flags = cpu_to_le32(FUNC_DRV_IF_CHANGE_REQ_FLAGS_UP); + + return bnge_hwrm_req_send(bd, req); +} + static int bnge_open_core(struct bnge_net *bn) { struct bnge_dev *bd = bn->bd; @@ -2168,16 +2888,22 @@ static int bnge_open_core(struct bnge_net *bn) netif_carrier_off(bn->netdev); + rc = bnge_hwrm_if_change(bd, true); + if (rc) { + netdev_err(bn->netdev, "bnge_hwrm_if_change err: %d\n", rc); + return rc; + } + rc = bnge_reserve_rings(bd); if (rc) { netdev_err(bn->netdev, "bnge_reserve_rings err: %d\n", rc); - return rc; + goto err_if_change; } rc = bnge_alloc_core(bn); if (rc) { netdev_err(bn->netdev, "bnge_alloc_core err: %d\n", rc); - return rc; + goto err_if_change; } bnge_init_napi(bn); @@ -2192,7 +2918,31 @@ static int bnge_open_core(struct bnge_net *bn) netdev_err(bn->netdev, "bnge_init_nic err: %d\n", rc); goto err_free_irq; } + + bnge_enable_napi(bn); + + rc = bnge_update_phy_setting(bn); + if (rc) { + netdev_warn(bn->netdev, "failed to update PHY settings (rc: %d)\n", + rc); + WRITE_ONCE(bd->link_info.phy_retry_expires, jiffies + 5 * HZ); + WRITE_ONCE(bd->link_info.phy_retry, true); + } else { + WRITE_ONCE(bd->link_info.phy_retry, false); + } + set_bit(BNGE_STATE_OPEN, &bd->state); + set_bit(BNGE_STATE_STATS_ENABLE, &bn->state); + + bnge_enable_int(bn); + + bnge_tx_enable(bn); + + mod_timer(&bn->timer, jiffies + bn->current_interval); + + /* Poll link status and check for SFP+ module status */ + bnge_get_port_module_status(bn); + return 0; err_free_irq: @@ -2200,16 +2950,11 @@ err_free_irq: err_del_napi: bnge_del_napi(bn); bnge_free_core(bn); +err_if_change: + bnge_hwrm_if_change(bd, false); return rc; } -static netdev_tx_t bnge_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - dev_kfree_skb_any(skb); - - return NETDEV_TX_OK; -} - static int bnge_open(struct net_device *dev) { struct bnge_net *bn = netdev_priv(dev); @@ -2224,17 +2969,206 @@ static int bnge_open(struct net_device *dev) static int bnge_shutdown_nic(struct bnge_net *bn) { - /* TODO: close_path = 0 until we make NAPI functional */ - bnge_hwrm_resource_free(bn, 0); + bnge_hwrm_resource_free(bn, 1); return 0; } +static void bnge_add_prev_ring_stats64(struct bnge_net *bn, + struct rtnl_link_stats64 *stats) +{ + struct netdev_queue_stats_rx *rx_save = &bn->rxq_prv_stats; + struct netdev_queue_stats_tx *tx_save = &bn->txq_prv_stats; + struct rtnl_link_stats64 *stats64_save = &bn->prv_stats64; + + stats->rx_packets += rx_save->packets; + stats->tx_packets += tx_save->packets; + stats->rx_bytes += rx_save->bytes; + stats->tx_bytes += tx_save->bytes; + stats->rx_missed_errors += rx_save->hw_drop_overruns; + stats->tx_dropped += tx_save->hw_drop_errors; + + stats->multicast += stats64_save->multicast; +} + +static void bnge_get_ring_stats64(struct bnge_dev *bd, + struct rtnl_link_stats64 *stats) +{ + struct bnge_net *bn = netdev_priv(bd->netdev); + int i; + + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + u64 tx_bytes, tx_packets, tx_dropped; + u64 multicast, rx_missed_errors; + struct bnge_nq_ring_info *nqr; + u64 rx_bytes, rx_packets; + unsigned int start; + u64 *sw; + + nqr = &bnapi->nq_ring; + sw = nqr->stats.sw_stats; + + do { + start = u64_stats_fetch_begin(&nqr->stats.syncp); + + rx_packets = BNGE_GET_RING_STATS64(sw, rx_ucast_pkts); + rx_packets += BNGE_GET_RING_STATS64(sw, rx_mcast_pkts); + rx_packets += BNGE_GET_RING_STATS64(sw, rx_bcast_pkts); + + tx_packets = BNGE_GET_RING_STATS64(sw, tx_ucast_pkts); + tx_packets += BNGE_GET_RING_STATS64(sw, tx_mcast_pkts); + tx_packets += BNGE_GET_RING_STATS64(sw, tx_bcast_pkts); + + rx_bytes = BNGE_GET_RING_STATS64(sw, rx_ucast_bytes); + rx_bytes += BNGE_GET_RING_STATS64(sw, rx_mcast_bytes); + rx_bytes += BNGE_GET_RING_STATS64(sw, rx_bcast_bytes); + + tx_bytes = BNGE_GET_RING_STATS64(sw, tx_ucast_bytes); + tx_bytes += BNGE_GET_RING_STATS64(sw, tx_mcast_bytes); + tx_bytes += BNGE_GET_RING_STATS64(sw, tx_bcast_bytes); + + multicast = BNGE_GET_RING_STATS64(sw, rx_mcast_pkts); + rx_missed_errors = + BNGE_GET_RING_STATS64(sw, rx_discard_pkts); + tx_dropped = + BNGE_GET_RING_STATS64(sw, tx_error_pkts); + } while (u64_stats_fetch_retry(&nqr->stats.syncp, start)); + + stats->rx_packets += rx_packets; + stats->tx_packets += tx_packets; + stats->rx_bytes += rx_bytes; + stats->tx_bytes += tx_bytes; + stats->multicast += multicast; + stats->rx_missed_errors += rx_missed_errors; + stats->tx_dropped += tx_dropped; + } +} + +static void bnge_get_port_stats64(struct bnge_net *bn, + struct rtnl_link_stats64 *stats) +{ + unsigned int start; + u64 *tx, *rx; + + rx = bn->port_stats.sw_stats; + tx = bn->port_stats.sw_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8; + + do { + start = u64_stats_fetch_begin(&bn->port_stats.syncp); + + stats->rx_crc_errors = + BNGE_GET_RX_PORT_STATS64(rx, rx_fcs_err_frames); + stats->rx_frame_errors = + BNGE_GET_RX_PORT_STATS64(rx, rx_align_err_frames); + stats->rx_length_errors = + BNGE_GET_RX_PORT_STATS64(rx, rx_undrsz_frames) + + BNGE_GET_RX_PORT_STATS64(rx, rx_ovrsz_frames) + + BNGE_GET_RX_PORT_STATS64(rx, rx_runt_frames); + stats->rx_errors = + BNGE_GET_RX_PORT_STATS64(rx, rx_false_carrier_frames) + + BNGE_GET_RX_PORT_STATS64(rx, rx_jbr_frames); + stats->collisions = + BNGE_GET_TX_PORT_STATS64(tx, tx_total_collisions); + stats->tx_fifo_errors = + BNGE_GET_TX_PORT_STATS64(tx, tx_fifo_underruns); + stats->tx_errors = BNGE_GET_TX_PORT_STATS64(tx, tx_err); + } while (u64_stats_fetch_retry(&bn->port_stats.syncp, start)); +} + +static void bnge_fill_prev_stats64(struct bnge_net *bn, + struct rtnl_link_stats64 *stats) +{ + struct netdev_queue_stats_rx *rx_save = &bn->rxq_prv_stats; + struct netdev_queue_stats_tx *tx_save = &bn->txq_prv_stats; + struct rtnl_link_stats64 *stats64_save = &bn->prv_stats64; + + stats->rx_packets = rx_save->packets; + stats->tx_packets = tx_save->packets; + stats->rx_bytes = rx_save->bytes; + stats->tx_bytes = tx_save->bytes; + stats->rx_missed_errors = rx_save->hw_drop_overruns; + stats->tx_dropped = tx_save->hw_drop_errors; + stats->multicast = stats64_save->multicast; +} + +static void bnge_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct bnge_net *bn = netdev_priv(dev); + + if (bn->flags & BNGE_FLAG_PORT_STATS) + bnge_get_port_stats64(bn, stats); + + spin_lock_bh(&bn->stats_lock); + if (!test_bit(BNGE_STATE_STATS_ENABLE, &bn->state)) { + bnge_fill_prev_stats64(bn, stats); + spin_unlock_bh(&bn->stats_lock); + return; + } + + bnge_get_ring_stats64(bn->bd, stats); + bnge_add_prev_ring_stats64(bn, stats); + spin_unlock_bh(&bn->stats_lock); +} + +static void bnge_save_ring_stats(struct bnge_net *bn) +{ + struct netdev_queue_stats_rx *rx_save = &bn->rxq_prv_stats; + struct netdev_queue_stats_tx *tx_save = &bn->txq_prv_stats; + struct rtnl_link_stats64 *stats64_save = &bn->prv_stats64; + int i; + + for (i = 0; i < bn->bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + struct bnge_nq_ring_info *nqr; + u64 *sw; + + nqr = &bnapi->nq_ring; + sw = nqr->stats.sw_stats; + + rx_save->packets += BNGE_GET_RING_STATS64(sw, rx_ucast_pkts); + rx_save->packets += BNGE_GET_RING_STATS64(sw, rx_mcast_pkts); + rx_save->packets += BNGE_GET_RING_STATS64(sw, rx_bcast_pkts); + rx_save->bytes += BNGE_GET_RING_STATS64(sw, rx_ucast_bytes); + rx_save->bytes += BNGE_GET_RING_STATS64(sw, rx_mcast_bytes); + rx_save->bytes += BNGE_GET_RING_STATS64(sw, rx_bcast_bytes); + rx_save->hw_drop_overruns += BNGE_GET_RING_STATS64(sw, rx_discard_pkts); + rx_save->hw_drops += BNGE_GET_RING_STATS64(sw, rx_error_pkts) + + BNGE_GET_RING_STATS64(sw, rx_discard_pkts); + + tx_save->packets += BNGE_GET_RING_STATS64(sw, tx_ucast_pkts); + tx_save->packets += BNGE_GET_RING_STATS64(sw, tx_mcast_pkts); + tx_save->packets += BNGE_GET_RING_STATS64(sw, tx_bcast_pkts); + tx_save->bytes += BNGE_GET_RING_STATS64(sw, tx_ucast_bytes); + tx_save->bytes += BNGE_GET_RING_STATS64(sw, tx_mcast_bytes); + tx_save->bytes += BNGE_GET_RING_STATS64(sw, tx_bcast_bytes); + tx_save->hw_drop_errors += BNGE_GET_RING_STATS64(sw, tx_error_pkts); + tx_save->hw_drops += BNGE_GET_RING_STATS64(sw, tx_discard_pkts) + + BNGE_GET_RING_STATS64(sw, tx_error_pkts); + + stats64_save->multicast += + BNGE_GET_RING_STATS64(sw, rx_mcast_pkts); + } +} + static void bnge_close_core(struct bnge_net *bn) { struct bnge_dev *bd = bn->bd; + bnge_tx_disable(bn); + clear_bit(BNGE_STATE_OPEN, &bd->state); + + timer_delete_sync(&bn->timer); bnge_shutdown_nic(bn); + bnge_disable_napi(bn); + + /* Save ring stats before shutdown */ + spin_lock_bh(&bn->stats_lock); + bnge_save_ring_stats(bn); + clear_bit(BNGE_STATE_STATS_ENABLE, &bn->state); + spin_unlock_bh(&bn->stats_lock); + bnge_free_all_rings_bufs(bn); bnge_free_irq(bn); bnge_del_napi(bn); @@ -2247,14 +3181,99 @@ static int bnge_close(struct net_device *dev) struct bnge_net *bn = netdev_priv(dev); bnge_close_core(bn); + bnge_hwrm_shutdown_link(bn->bd); + bnge_hwrm_if_change(bn->bd, false); + bn->sp_event = 0; return 0; } +static void bnge_get_queue_stats_rx(struct net_device *dev, int i, + struct netdev_queue_stats_rx *stats) +{ + struct bnge_net *bn = netdev_priv(dev); + struct bnge_nq_ring_info *nqr; + u64 *sw; + + if (!bn->bnapi) + return; + + nqr = &bn->bnapi[i]->nq_ring; + sw = nqr->stats.sw_stats; + + stats->packets = 0; + stats->packets += BNGE_GET_RING_STATS64(sw, rx_ucast_pkts); + stats->packets += BNGE_GET_RING_STATS64(sw, rx_mcast_pkts); + stats->packets += BNGE_GET_RING_STATS64(sw, rx_bcast_pkts); + + stats->bytes = 0; + stats->bytes += BNGE_GET_RING_STATS64(sw, rx_ucast_bytes); + stats->bytes += BNGE_GET_RING_STATS64(sw, rx_mcast_bytes); + stats->bytes += BNGE_GET_RING_STATS64(sw, rx_bcast_bytes); + + stats->hw_drop_overruns = BNGE_GET_RING_STATS64(sw, rx_discard_pkts); + stats->hw_drops = BNGE_GET_RING_STATS64(sw, rx_error_pkts) + + stats->hw_drop_overruns; +} + +static void bnge_get_queue_stats_tx(struct net_device *dev, int i, + struct netdev_queue_stats_tx *stats) +{ + struct bnge_net *bn = netdev_priv(dev); + struct bnge_napi *bnapi; + u64 *sw; + + if (!bn->tx_ring) + return; + + bnapi = bn->tx_ring[bn->tx_ring_map[i]].bnapi; + sw = bnapi->nq_ring.stats.sw_stats; + + stats->packets = 0; + stats->packets += BNGE_GET_RING_STATS64(sw, tx_ucast_pkts); + stats->packets += BNGE_GET_RING_STATS64(sw, tx_mcast_pkts); + stats->packets += BNGE_GET_RING_STATS64(sw, tx_bcast_pkts); + + stats->bytes = 0; + stats->bytes += BNGE_GET_RING_STATS64(sw, tx_ucast_bytes); + stats->bytes += BNGE_GET_RING_STATS64(sw, tx_mcast_bytes); + stats->bytes += BNGE_GET_RING_STATS64(sw, tx_bcast_bytes); + + stats->hw_drop_errors = BNGE_GET_RING_STATS64(sw, tx_error_pkts); + stats->hw_drops = BNGE_GET_RING_STATS64(sw, tx_discard_pkts) + + stats->hw_drop_errors; +} + +static void bnge_get_base_stats(struct net_device *dev, + struct netdev_queue_stats_rx *rx, + struct netdev_queue_stats_tx *tx) +{ + struct bnge_net *bn = netdev_priv(dev); + + rx->packets = bn->rxq_prv_stats.packets; + rx->bytes = bn->rxq_prv_stats.bytes; + rx->hw_drops = bn->rxq_prv_stats.hw_drops; + rx->hw_drop_overruns = bn->rxq_prv_stats.hw_drop_overruns; + + tx->packets = bn->txq_prv_stats.packets; + tx->bytes = bn->txq_prv_stats.bytes; + tx->hw_drops = bn->txq_prv_stats.hw_drops; + tx->hw_drop_errors = bn->txq_prv_stats.hw_drop_errors; +} + +static const struct netdev_stat_ops bnge_stat_ops = { + .get_queue_stats_rx = bnge_get_queue_stats_rx, + .get_queue_stats_tx = bnge_get_queue_stats_tx, + .get_base_stats = bnge_get_base_stats, +}; + static const struct net_device_ops bnge_netdev_ops = { .ndo_open = bnge_open, .ndo_stop = bnge_close, .ndo_start_xmit = bnge_start_xmit, + .ndo_get_stats64 = bnge_get_stats64, + .ndo_set_rx_mode_async = bnge_set_rx_mode, + .ndo_features_check = bnge_features_check, }; static void bnge_init_mac_addr(struct bnge_dev *bd) @@ -2295,7 +3314,6 @@ void bnge_set_ring_params(struct bnge_dev *bd) rx_space = rx_size + ALIGN(NET_SKB_PAD, 8) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - bn->rx_copy_thresh = BNGE_RX_COPY_THRESH; ring_size = bn->rx_ring_size; bn->rx_agg_ring_size = 0; bn->rx_agg_nr_pages = 0; @@ -2334,7 +3352,10 @@ void bnge_set_ring_params(struct bnge_dev *bd) bn->rx_agg_ring_size = agg_ring_size; bn->rx_agg_ring_mask = (bn->rx_agg_nr_pages * RX_DESC_CNT) - 1; - rx_size = SKB_DATA_ALIGN(BNGE_RX_COPY_THRESH + NET_IP_ALIGN); + rx_size = max3(BNGE_DEFAULT_RX_COPYBREAK, + bn->rx_copybreak, + bn->netdev->cfg_pending->hds_thresh); + rx_size = SKB_DATA_ALIGN(rx_size + NET_IP_ALIGN); rx_space = rx_size + NET_SKB_PAD + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); } @@ -2367,6 +3388,17 @@ void bnge_set_ring_params(struct bnge_dev *bd) bn->cp_ring_mask = bn->cp_bit - 1; } +static void bnge_init_ring_params(struct bnge_net *bn) +{ + u32 rx_size; + + bn->rx_copybreak = BNGE_DEFAULT_RX_COPYBREAK; + /* Try to fit 4 chunks into a 4k page */ + rx_size = SZ_1K - + NET_SKB_PAD - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + bn->netdev->cfg->hds_thresh = max(BNGE_DEFAULT_RX_COPYBREAK, rx_size); +} + int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs) { struct net_device *netdev; @@ -2382,6 +3414,7 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs) bd->netdev = netdev; netdev->netdev_ops = &bnge_netdev_ops; + netdev->stat_ops = &bnge_stat_ops; bnge_set_ethtool_ops(netdev); @@ -2451,25 +3484,51 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs) if (bd->tso_max_segs) netif_set_tso_max_segs(netdev, bd->tso_max_segs); + INIT_WORK(&bn->sp_task, bnge_sp_task); + timer_setup(&bn->timer, bnge_timer, 0); + bn->current_interval = BNGE_TIMER_INTERVAL; + + bn->bnge_pf_wq = alloc_ordered_workqueue("bnge_pf_wq-%s", 0, + dev_name(bd->dev)); + if (!bn->bnge_pf_wq) { + netdev_err(netdev, "Unable to create workqueue.\n"); + rc = -ENOMEM; + goto err_netdev; + } + bn->rx_ring_size = BNGE_DEFAULT_RX_RING_SIZE; bn->tx_ring_size = BNGE_DEFAULT_TX_RING_SIZE; bn->rx_dir = DMA_FROM_DEVICE; bnge_set_tpa_flags(bd); + bnge_init_ring_params(bn); bnge_set_ring_params(bd); bnge_init_l2_fltr_tbl(bn); bnge_init_mac_addr(bd); + rc = bnge_probe_phy(bn, true); + if (rc) + goto err_free_workq; + + rc = bnge_alloc_port_stats(bn); + if (rc) + goto err_free_workq; + spin_lock_init(&bn->stats_lock); + netdev->request_ops_lock = true; rc = register_netdev(netdev); if (rc) { dev_err(bd->dev, "Register netdev failed rc: %d\n", rc); - goto err_netdev; + goto err_free_port_stats; } return 0; +err_free_port_stats: + bnge_free_port_stats(bn); +err_free_workq: + destroy_workqueue(bn->bnge_pf_wq); err_netdev: free_netdev(netdev); return rc; @@ -2478,8 +3537,19 @@ err_netdev: void bnge_netdev_free(struct bnge_dev *bd) { struct net_device *netdev = bd->netdev; + struct bnge_net *bn; + + bn = netdev_priv(netdev); unregister_netdev(netdev); + + timer_shutdown_sync(&bn->timer); + cancel_work_sync(&bn->sp_task); + bn->sp_event = 0; + destroy_workqueue(bn->bnge_pf_wq); + + bnge_free_port_stats(bn); + free_netdev(netdev); bd->netdev = NULL; } |
