diff options
Diffstat (limited to 'drivers/net/ethernet/broadcom/bnge/bnge_netdev.c')
-rw-r--r-- | drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 2217 |
1 files changed, 2217 insertions, 0 deletions
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index 02254934f3d0..832eeb960bd2 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -14,10 +14,2194 @@ #include <linux/if.h> #include <net/ip.h> #include <linux/skbuff.h> +#include <net/page_pool/helpers.h> #include "bnge.h" #include "bnge_hwrm_lib.h" #include "bnge_ethtool.h" +#include "bnge_rmem.h" + +#define BNGE_RING_TO_TC_OFF(bd, tx) \ + ((tx) % (bd)->tx_nr_rings_per_tc) + +#define BNGE_RING_TO_TC(bd, tx) \ + ((tx) / (bd)->tx_nr_rings_per_tc) + +#define BNGE_TC_TO_RING_BASE(bd, tc) \ + ((tc) * (bd)->tx_nr_rings_per_tc) + +static void bnge_free_stats_mem(struct bnge_net *bn, + struct bnge_stats_mem *stats) +{ + struct bnge_dev *bd = bn->bd; + + if (stats->hw_stats) { + dma_free_coherent(bd->dev, stats->len, stats->hw_stats, + stats->hw_stats_map); + stats->hw_stats = NULL; + } +} + +static int bnge_alloc_stats_mem(struct bnge_net *bn, + struct bnge_stats_mem *stats) +{ + struct bnge_dev *bd = bn->bd; + + stats->hw_stats = dma_alloc_coherent(bd->dev, stats->len, + &stats->hw_stats_map, GFP_KERNEL); + if (!stats->hw_stats) + return -ENOMEM; + + return 0; +} + +static void bnge_free_ring_stats(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 = &bnapi->nq_ring; + + bnge_free_stats_mem(bn, &nqr->stats); + } +} + +static int bnge_alloc_ring_stats(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + u32 size, i; + int rc; + + size = bd->hw_ring_stats_size; + + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + struct bnge_nq_ring_info *nqr = &bnapi->nq_ring; + + nqr->stats.len = size; + rc = bnge_alloc_stats_mem(bn, &nqr->stats); + if (rc) + goto err_free_ring_stats; + + nqr->hw_stats_ctx_id = INVALID_STATS_CTX_ID; + } + return 0; + +err_free_ring_stats: + bnge_free_ring_stats(bn); + return rc; +} + +static void bnge_free_nq_desc_arr(struct bnge_nq_ring_info *nqr) +{ + struct bnge_ring_struct *ring = &nqr->ring_struct; + + kfree(nqr->desc_ring); + nqr->desc_ring = NULL; + ring->ring_mem.pg_arr = NULL; + kfree(nqr->desc_mapping); + nqr->desc_mapping = NULL; + ring->ring_mem.dma_arr = NULL; +} + +static void bnge_free_cp_desc_arr(struct bnge_cp_ring_info *cpr) +{ + struct bnge_ring_struct *ring = &cpr->ring_struct; + + kfree(cpr->desc_ring); + cpr->desc_ring = NULL; + ring->ring_mem.pg_arr = NULL; + kfree(cpr->desc_mapping); + cpr->desc_mapping = NULL; + ring->ring_mem.dma_arr = NULL; +} + +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); + if (!nqr->desc_ring) + return -ENOMEM; + + nqr->desc_mapping = kcalloc(n, sizeof(*nqr->desc_mapping), GFP_KERNEL); + if (!nqr->desc_mapping) + goto err_free_desc_ring; + return 0; + +err_free_desc_ring: + kfree(nqr->desc_ring); + nqr->desc_ring = NULL; + return -ENOMEM; +} + +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); + if (!cpr->desc_ring) + return -ENOMEM; + + cpr->desc_mapping = kcalloc(n, sizeof(*cpr->desc_mapping), GFP_KERNEL); + if (!cpr->desc_mapping) + goto err_free_desc_ring; + return 0; + +err_free_desc_ring: + kfree(cpr->desc_ring); + cpr->desc_ring = NULL; + return -ENOMEM; +} + +static void bnge_free_nq_arrays(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]; + + bnge_free_nq_desc_arr(&bnapi->nq_ring); + } +} + +static int bnge_alloc_nq_arrays(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i, rc; + + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + + rc = bnge_alloc_nq_desc_arr(&bnapi->nq_ring, bn->cp_nr_pages); + if (rc) + goto err_free_nq_arrays; + } + return 0; + +err_free_nq_arrays: + bnge_free_nq_arrays(bn); + return rc; +} + +static void bnge_free_nq_tree(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; + struct bnge_ring_struct *ring; + int j; + + nqr = &bnapi->nq_ring; + ring = &nqr->ring_struct; + + bnge_free_ring(bd, &ring->ring_mem); + + if (!nqr->cp_ring_arr) + continue; + + for (j = 0; j < nqr->cp_ring_count; j++) { + struct bnge_cp_ring_info *cpr = &nqr->cp_ring_arr[j]; + + ring = &cpr->ring_struct; + bnge_free_ring(bd, &ring->ring_mem); + bnge_free_cp_desc_arr(cpr); + } + kfree(nqr->cp_ring_arr); + nqr->cp_ring_arr = NULL; + nqr->cp_ring_count = 0; + } +} + +static int alloc_one_cp_ring(struct bnge_net *bn, + struct bnge_cp_ring_info *cpr) +{ + struct bnge_ring_mem_info *rmem; + struct bnge_ring_struct *ring; + struct bnge_dev *bd = bn->bd; + int rc; + + rc = bnge_alloc_cp_desc_arr(cpr, bn->cp_nr_pages); + if (rc) + return -ENOMEM; + ring = &cpr->ring_struct; + rmem = &ring->ring_mem; + rmem->nr_pages = bn->cp_nr_pages; + rmem->page_size = HW_CMPD_RING_SIZE; + rmem->pg_arr = (void **)cpr->desc_ring; + rmem->dma_arr = cpr->desc_mapping; + rmem->flags = BNGE_RMEM_RING_PTE_FLAG; + rc = bnge_alloc_ring(bd, rmem); + if (rc) + goto err_free_cp_desc_arr; + return rc; + +err_free_cp_desc_arr: + bnge_free_cp_desc_arr(cpr); + return rc; +} + +static int bnge_alloc_nq_tree(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i, j, ulp_msix, rc; + int tcs = 1; + + ulp_msix = bnge_aux_get_msix(bd); + for (i = 0, j = 0; i < bd->nq_nr_rings; i++) { + bool sh = !!(bd->flags & BNGE_EN_SHARED_CHNL); + struct bnge_napi *bnapi = bn->bnapi[i]; + struct bnge_nq_ring_info *nqr; + struct bnge_cp_ring_info *cpr; + struct bnge_ring_struct *ring; + int cp_count = 0, k; + int rx = 0, tx = 0; + + nqr = &bnapi->nq_ring; + nqr->bnapi = bnapi; + ring = &nqr->ring_struct; + + rc = bnge_alloc_ring(bd, &ring->ring_mem); + if (rc) + goto err_free_nq_tree; + + ring->map_idx = ulp_msix + i; + + if (i < bd->rx_nr_rings) { + cp_count++; + rx = 1; + } + + if ((sh && i < bd->tx_nr_rings) || + (!sh && i >= bd->rx_nr_rings)) { + cp_count += tcs; + tx = 1; + } + + nqr->cp_ring_arr = kcalloc(cp_count, sizeof(*cpr), + GFP_KERNEL); + if (!nqr->cp_ring_arr) { + rc = -ENOMEM; + goto err_free_nq_tree; + } + + nqr->cp_ring_count = cp_count; + + for (k = 0; k < cp_count; k++) { + cpr = &nqr->cp_ring_arr[k]; + rc = alloc_one_cp_ring(bn, cpr); + if (rc) + goto err_free_nq_tree; + + cpr->bnapi = bnapi; + cpr->cp_idx = k; + if (!k && rx) { + bn->rx_ring[i].rx_cpr = cpr; + cpr->cp_ring_type = BNGE_NQ_HDL_TYPE_RX; + } else { + int n, tc = k - rx; + + n = BNGE_TC_TO_RING_BASE(bd, tc) + j; + bn->tx_ring[n].tx_cpr = cpr; + cpr->cp_ring_type = BNGE_NQ_HDL_TYPE_TX; + } + } + if (tx) + j++; + } + return 0; + +err_free_nq_tree: + bnge_free_nq_tree(bn); + return rc; +} + +static bool bnge_separate_head_pool(struct bnge_rx_ring_info *rxr) +{ + return rxr->need_head_pool || PAGE_SIZE > BNGE_RX_PAGE_SIZE; +} + +static void bnge_free_one_rx_ring_bufs(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + int i, max_idx; + + if (!rxr->rx_buf_ring) + return; + + max_idx = bn->rx_nr_pages * RX_DESC_CNT; + + for (i = 0; i < max_idx; i++) { + struct bnge_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[i]; + void *data = rx_buf->data; + + if (!data) + continue; + + rx_buf->data = NULL; + page_pool_free_va(rxr->head_pool, data, true); + } +} + +static void bnge_free_one_agg_ring_bufs(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + int i, max_idx; + + if (!rxr->rx_agg_buf_ring) + return; + + max_idx = bn->rx_agg_nr_pages * RX_DESC_CNT; + + for (i = 0; i < max_idx; i++) { + struct bnge_sw_rx_agg_bd *rx_agg_buf = &rxr->rx_agg_buf_ring[i]; + netmem_ref netmem = rx_agg_buf->netmem; + + if (!netmem) + continue; + + rx_agg_buf->netmem = 0; + __clear_bit(i, rxr->rx_agg_bmap); + + page_pool_recycle_direct_netmem(rxr->page_pool, netmem); + } +} + +static void bnge_free_one_rx_ring_pair_bufs(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + bnge_free_one_rx_ring_bufs(bn, rxr); + bnge_free_one_agg_ring_bufs(bn, rxr); +} + +static void bnge_free_rx_ring_pair_bufs(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + if (!bn->rx_ring) + return; + + for (i = 0; i < bd->rx_nr_rings; i++) + bnge_free_one_rx_ring_pair_bufs(bn, &bn->rx_ring[i]); +} + +static void bnge_free_all_rings_bufs(struct bnge_net *bn) +{ + bnge_free_rx_ring_pair_bufs(bn); +} + +static void bnge_free_rx_rings(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + for (i = 0; i < bd->rx_nr_rings; i++) { + struct bnge_rx_ring_info *rxr = &bn->rx_ring[i]; + struct bnge_ring_struct *ring; + + page_pool_destroy(rxr->page_pool); + page_pool_destroy(rxr->head_pool); + rxr->page_pool = rxr->head_pool = NULL; + + kfree(rxr->rx_agg_bmap); + rxr->rx_agg_bmap = NULL; + + ring = &rxr->rx_ring_struct; + bnge_free_ring(bd, &ring->ring_mem); + + ring = &rxr->rx_agg_ring_struct; + bnge_free_ring(bd, &ring->ring_mem); + } +} + +static int bnge_alloc_rx_page_pool(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr, + int numa_node) +{ + const unsigned int agg_size_fac = PAGE_SIZE / BNGE_RX_PAGE_SIZE; + const unsigned int rx_size_fac = PAGE_SIZE / SZ_4K; + struct page_pool_params pp = { 0 }; + struct bnge_dev *bd = bn->bd; + struct page_pool *pool; + + pp.pool_size = bn->rx_agg_ring_size / agg_size_fac; + pp.nid = numa_node; + pp.netdev = bn->netdev; + pp.dev = bd->dev; + pp.dma_dir = bn->rx_dir; + pp.max_len = PAGE_SIZE; + pp.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV | + PP_FLAG_ALLOW_UNREADABLE_NETMEM; + pp.queue_idx = rxr->bnapi->index; + + pool = page_pool_create(&pp); + if (IS_ERR(pool)) + return PTR_ERR(pool); + rxr->page_pool = pool; + + rxr->need_head_pool = page_pool_is_unreadable(pool); + if (bnge_separate_head_pool(rxr)) { + pp.pool_size = min(bn->rx_ring_size / rx_size_fac, 1024); + pp.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; + pool = page_pool_create(&pp); + if (IS_ERR(pool)) + goto err_destroy_pp; + } else { + page_pool_get(pool); + } + rxr->head_pool = pool; + return 0; + +err_destroy_pp: + page_pool_destroy(rxr->page_pool); + rxr->page_pool = NULL; + return PTR_ERR(pool); +} + +static void bnge_enable_rx_page_pool(struct bnge_rx_ring_info *rxr) +{ + page_pool_enable_direct_recycling(rxr->head_pool, &rxr->bnapi->napi); + page_pool_enable_direct_recycling(rxr->page_pool, &rxr->bnapi->napi); +} + +static int bnge_alloc_rx_agg_bmap(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + u16 mem_size; + + rxr->rx_agg_bmap_size = bn->rx_agg_ring_mask + 1; + mem_size = rxr->rx_agg_bmap_size / 8; + rxr->rx_agg_bmap = kzalloc(mem_size, GFP_KERNEL); + if (!rxr->rx_agg_bmap) + return -ENOMEM; + + return 0; +} + +static int bnge_alloc_rx_rings(struct bnge_net *bn) +{ + int i, rc = 0, agg_rings = 0, cpu; + struct bnge_dev *bd = bn->bd; + + if (bnge_is_agg_reqd(bd)) + agg_rings = 1; + + for (i = 0; i < bd->rx_nr_rings; i++) { + struct bnge_rx_ring_info *rxr = &bn->rx_ring[i]; + struct bnge_ring_struct *ring; + int cpu_node; + + ring = &rxr->rx_ring_struct; + + cpu = cpumask_local_spread(i, dev_to_node(bd->dev)); + cpu_node = cpu_to_node(cpu); + netdev_dbg(bn->netdev, "Allocating page pool for rx_ring[%d] on numa_node: %d\n", + i, cpu_node); + rc = bnge_alloc_rx_page_pool(bn, rxr, cpu_node); + if (rc) + goto err_free_rx_rings; + bnge_enable_rx_page_pool(rxr); + + rc = bnge_alloc_ring(bd, &ring->ring_mem); + if (rc) + goto err_free_rx_rings; + + ring->grp_idx = i; + if (agg_rings) { + ring = &rxr->rx_agg_ring_struct; + rc = bnge_alloc_ring(bd, &ring->ring_mem); + if (rc) + goto err_free_rx_rings; + + ring->grp_idx = i; + rc = bnge_alloc_rx_agg_bmap(bn, rxr); + if (rc) + goto err_free_rx_rings; + } + } + return rc; + +err_free_rx_rings: + bnge_free_rx_rings(bn); + return rc; +} + +static void bnge_free_tx_rings(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + for (i = 0; i < bd->tx_nr_rings; i++) { + struct bnge_tx_ring_info *txr = &bn->tx_ring[i]; + struct bnge_ring_struct *ring; + + ring = &txr->tx_ring_struct; + + bnge_free_ring(bd, &ring->ring_mem); + } +} + +static int bnge_alloc_tx_rings(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i, j, rc; + + for (i = 0, j = 0; i < bd->tx_nr_rings; i++) { + struct bnge_tx_ring_info *txr = &bn->tx_ring[i]; + struct bnge_ring_struct *ring; + u8 qidx; + + ring = &txr->tx_ring_struct; + + rc = bnge_alloc_ring(bd, &ring->ring_mem); + if (rc) + goto err_free_tx_rings; + + ring->grp_idx = txr->bnapi->index; + qidx = bd->tc_to_qidx[j]; + ring->queue_id = bd->q_info[qidx].queue_id; + if (BNGE_RING_TO_TC_OFF(bd, i) == (bd->tx_nr_rings_per_tc - 1)) + j++; + } + return 0; + +err_free_tx_rings: + bnge_free_tx_rings(bn); + return rc; +} + +static void bnge_free_vnic_attributes(struct bnge_net *bn) +{ + struct pci_dev *pdev = bn->bd->pdev; + struct bnge_vnic_info *vnic; + int i; + + if (!bn->vnic_info) + return; + + for (i = 0; i < bn->nr_vnics; i++) { + vnic = &bn->vnic_info[i]; + + kfree(vnic->uc_list); + vnic->uc_list = NULL; + + if (vnic->mc_list) { + dma_free_coherent(&pdev->dev, vnic->mc_list_size, + vnic->mc_list, vnic->mc_list_mapping); + vnic->mc_list = NULL; + } + + if (vnic->rss_table) { + dma_free_coherent(&pdev->dev, vnic->rss_table_size, + vnic->rss_table, + vnic->rss_table_dma_addr); + vnic->rss_table = NULL; + } + + vnic->rss_hash_key = NULL; + vnic->flags = 0; + } +} + +static int bnge_alloc_vnic_attributes(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + struct bnge_vnic_info *vnic; + int i, size; + + for (i = 0; i < bn->nr_vnics; i++) { + vnic = &bn->vnic_info[i]; + + if (vnic->flags & BNGE_VNIC_UCAST_FLAG) { + int mem_size = (BNGE_MAX_UC_ADDRS - 1) * ETH_ALEN; + + vnic->uc_list = kmalloc(mem_size, GFP_KERNEL); + if (!vnic->uc_list) + goto err_free_vnic_attributes; + } + + if (vnic->flags & BNGE_VNIC_MCAST_FLAG) { + vnic->mc_list_size = BNGE_MAX_MC_ADDRS * ETH_ALEN; + vnic->mc_list = + dma_alloc_coherent(bd->dev, + vnic->mc_list_size, + &vnic->mc_list_mapping, + GFP_KERNEL); + if (!vnic->mc_list) + goto err_free_vnic_attributes; + } + + /* Allocate rss table and hash key */ + size = L1_CACHE_ALIGN(BNGE_MAX_RSS_TABLE_SIZE); + + vnic->rss_table_size = size + HW_HASH_KEY_SIZE; + vnic->rss_table = dma_alloc_coherent(bd->dev, + vnic->rss_table_size, + &vnic->rss_table_dma_addr, + GFP_KERNEL); + if (!vnic->rss_table) + goto err_free_vnic_attributes; + + vnic->rss_hash_key = ((void *)vnic->rss_table) + size; + vnic->rss_hash_key_dma_addr = vnic->rss_table_dma_addr + size; + } + return 0; + +err_free_vnic_attributes: + bnge_free_vnic_attributes(bn); + return -ENOMEM; +} + +static int bnge_alloc_vnics(struct bnge_net *bn) +{ + int num_vnics; + + /* Allocate only 1 VNIC for now + * Additional VNICs will be added based on RFS/NTUPLE in future patches + */ + num_vnics = 1; + + bn->vnic_info = kcalloc(num_vnics, sizeof(struct bnge_vnic_info), + GFP_KERNEL); + if (!bn->vnic_info) + return -ENOMEM; + + bn->nr_vnics = num_vnics; + + return 0; +} + +static void bnge_free_vnics(struct bnge_net *bn) +{ + kfree(bn->vnic_info); + bn->vnic_info = NULL; + bn->nr_vnics = 0; +} + +static void bnge_free_ring_grps(struct bnge_net *bn) +{ + kfree(bn->grp_info); + bn->grp_info = NULL; +} + +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); + if (!bn->grp_info) + return -ENOMEM; + for (i = 0; i < bd->nq_nr_rings; i++) { + bn->grp_info[i].fw_stats_ctx = INVALID_HW_RING_ID; + bn->grp_info[i].fw_grp_id = INVALID_HW_RING_ID; + bn->grp_info[i].rx_fw_ring_id = INVALID_HW_RING_ID; + bn->grp_info[i].agg_fw_ring_id = INVALID_HW_RING_ID; + bn->grp_info[i].nq_fw_ring_id = INVALID_HW_RING_ID; + } + + return 0; +} + +static void bnge_free_core(struct bnge_net *bn) +{ + bnge_free_vnic_attributes(bn); + bnge_free_tx_rings(bn); + bnge_free_rx_rings(bn); + bnge_free_nq_tree(bn); + bnge_free_nq_arrays(bn); + bnge_free_ring_stats(bn); + bnge_free_ring_grps(bn); + bnge_free_vnics(bn); + kfree(bn->tx_ring_map); + bn->tx_ring_map = NULL; + kfree(bn->tx_ring); + bn->tx_ring = NULL; + kfree(bn->rx_ring); + bn->rx_ring = NULL; + kfree(bn->bnapi); + bn->bnapi = NULL; +} + +static int bnge_alloc_core(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i, j, size, arr_size; + int rc = -ENOMEM; + void *bnapi; + + arr_size = L1_CACHE_ALIGN(sizeof(struct bnge_napi *) * + bd->nq_nr_rings); + size = L1_CACHE_ALIGN(sizeof(struct bnge_napi)); + bnapi = kzalloc(arr_size + size * bd->nq_nr_rings, GFP_KERNEL); + if (!bnapi) + return rc; + + bn->bnapi = bnapi; + bnapi += arr_size; + for (i = 0; i < bd->nq_nr_rings; i++, bnapi += size) { + struct bnge_nq_ring_info *nqr; + + bn->bnapi[i] = bnapi; + bn->bnapi[i]->index = i; + bn->bnapi[i]->bn = bn; + nqr = &bn->bnapi[i]->nq_ring; + 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); + if (!bn->rx_ring) + goto err_free_core; + + for (i = 0; i < bd->rx_nr_rings; i++) { + struct bnge_rx_ring_info *rxr = &bn->rx_ring[i]; + + rxr->rx_ring_struct.ring_mem.flags = + BNGE_RMEM_RING_PTE_FLAG; + rxr->rx_agg_ring_struct.ring_mem.flags = + BNGE_RMEM_RING_PTE_FLAG; + rxr->bnapi = bn->bnapi[i]; + 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); + if (!bn->tx_ring) + goto err_free_core; + + bn->tx_ring_map = kcalloc(bd->tx_nr_rings, sizeof(u16), + GFP_KERNEL); + if (!bn->tx_ring_map) + goto err_free_core; + + if (bd->flags & BNGE_EN_SHARED_CHNL) + j = 0; + else + j = bd->rx_nr_rings; + + for (i = 0; i < bd->tx_nr_rings; i++) { + struct bnge_tx_ring_info *txr = &bn->tx_ring[i]; + struct bnge_napi *bnapi2; + int k; + + txr->tx_ring_struct.ring_mem.flags = BNGE_RMEM_RING_PTE_FLAG; + bn->tx_ring_map[i] = i; + k = j + BNGE_RING_TO_TC_OFF(bd, i); + + bnapi2 = bn->bnapi[k]; + txr->txq_index = i; + txr->tx_napi_idx = + BNGE_RING_TO_TC(bd, txr->txq_index); + bnapi2->tx_ring[txr->tx_napi_idx] = txr; + txr->bnapi = bnapi2; + } + + rc = bnge_alloc_ring_stats(bn); + if (rc) + goto err_free_core; + + rc = bnge_alloc_vnics(bn); + if (rc) + goto err_free_core; + + rc = bnge_alloc_nq_arrays(bn); + if (rc) + goto err_free_core; + + bnge_init_ring_struct(bn); + + rc = bnge_alloc_rx_rings(bn); + if (rc) + goto err_free_core; + + rc = bnge_alloc_tx_rings(bn); + if (rc) + goto err_free_core; + + rc = bnge_alloc_nq_tree(bn); + if (rc) + goto err_free_core; + + bn->vnic_info[BNGE_VNIC_DEFAULT].flags |= BNGE_VNIC_RSS_FLAG | + BNGE_VNIC_MCAST_FLAG | + BNGE_VNIC_UCAST_FLAG; + rc = bnge_alloc_vnic_attributes(bn); + if (rc) + goto err_free_core; + return 0; + +err_free_core: + bnge_free_core(bn); + return rc; +} + +u16 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) +{ + return txr->tx_cpr->ring_struct.fw_ring_id; +} + +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 | + DB_RING_IDX(db, idx), db->doorbell); +} + +static void bnge_db_cq(struct bnge_net *bn, struct bnge_db_info *db, u32 idx) +{ + bnge_writeq(bn->bd, db->db_key64 | DBR_TYPE_CQ_ARMALL | + DB_RING_IDX(db, idx), db->doorbell); +} + +static int bnge_cp_num_to_irq_num(struct bnge_net *bn, int n) +{ + struct bnge_napi *bnapi = bn->bnapi[n]; + struct bnge_nq_ring_info *nqr; + + nqr = &bnapi->nq_ring; + + 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; + int i, j; + + for (i = 0; i < bd->nq_nr_rings; i++) { + 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; + 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; + } + } +} + +static netmem_ref __bnge_alloc_rx_netmem(struct bnge_net *bn, + dma_addr_t *mapping, + struct bnge_rx_ring_info *rxr, + unsigned int *offset, + gfp_t gfp) +{ + netmem_ref netmem; + + if (PAGE_SIZE > BNGE_RX_PAGE_SIZE) { + netmem = page_pool_alloc_frag_netmem(rxr->page_pool, offset, + BNGE_RX_PAGE_SIZE, gfp); + } else { + netmem = page_pool_alloc_netmems(rxr->page_pool, gfp); + *offset = 0; + } + if (!netmem) + return 0; + + *mapping = page_pool_get_dma_addr_netmem(netmem) + *offset; + 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) +{ + unsigned int offset; + struct page *page; + + page = page_pool_alloc_frag(rxr->head_pool, &offset, + bn->rx_buf_size, gfp); + if (!page) + return NULL; + + *mapping = page_pool_get_dma_addr(page) + bn->rx_dma_offset + offset; + 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) +{ + struct bnge_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[RING_RX(bn, prod)]; + struct rx_bd *rxbd; + dma_addr_t mapping; + u8 *data; + + rxbd = &rxr->rx_desc_ring[RX_RING(bn, prod)][RX_IDX(prod)]; + data = __bnge_alloc_rx_frag(bn, &mapping, rxr, gfp); + if (!data) + return -ENOMEM; + + rx_buf->data = data; + rx_buf->data_ptr = data + bn->rx_offset; + rx_buf->mapping = mapping; + + rxbd->rx_bd_haddr = cpu_to_le64(mapping); + + return 0; +} + +static int bnge_alloc_one_rx_ring_bufs(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr, + int ring_nr) +{ + u32 prod = rxr->rx_prod; + int i, rc = 0; + + for (i = 0; i < bn->rx_ring_size; i++) { + rc = bnge_alloc_rx_data(bn, rxr, prod, GFP_KERNEL); + if (rc) + break; + prod = NEXT_RX(prod); + } + + /* Abort if not a single buffer can be allocated */ + if (rc && !i) { + netdev_err(bn->netdev, + "RX ring %d: allocated %d/%d buffers, abort\n", + ring_nr, i, bn->rx_ring_size); + return rc; + } + + rxr->rx_prod = prod; + + if (i < bn->rx_ring_size) + netdev_warn(bn->netdev, + "RX ring %d: allocated %d/%d buffers, continuing\n", + ring_nr, i, bn->rx_ring_size); + return 0; +} + +static u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx) +{ + u16 next, max = rxr->rx_agg_bmap_size; + + next = find_next_zero_bit(rxr->rx_agg_bmap, max, idx); + if (next >= max) + next = find_first_zero_bit(rxr->rx_agg_bmap, max); + return next; +} + +static 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; + unsigned int offset = 0; + struct rx_bd *rxbd; + dma_addr_t mapping; + netmem_ref netmem; + + rxbd = &rxr->rx_agg_desc_ring[RX_AGG_RING(bn, prod)][RX_IDX(prod)]; + netmem = __bnge_alloc_rx_netmem(bn, &mapping, rxr, &offset, gfp); + if (!netmem) + return -ENOMEM; + + if (unlikely(test_bit(sw_prod, rxr->rx_agg_bmap))) + sw_prod = bnge_find_next_agg_idx(rxr, sw_prod); + + __set_bit(sw_prod, rxr->rx_agg_bmap); + rx_agg_buf = &rxr->rx_agg_buf_ring[sw_prod]; + rxr->rx_sw_agg_prod = RING_RX_AGG(bn, NEXT_RX_AGG(sw_prod)); + + rx_agg_buf->netmem = netmem; + rx_agg_buf->offset = offset; + rx_agg_buf->mapping = mapping; + rxbd->rx_bd_haddr = cpu_to_le64(mapping); + rxbd->rx_bd_opaque = sw_prod; + return 0; +} + +static int bnge_alloc_one_agg_ring_bufs(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr, + int ring_nr) +{ + u32 prod = rxr->rx_agg_prod; + int i, rc = 0; + + for (i = 0; i < bn->rx_agg_ring_size; i++) { + rc = bnge_alloc_rx_netmem(bn, rxr, prod, GFP_KERNEL); + if (rc) + break; + prod = NEXT_RX_AGG(prod); + } + + if (rc && i < MAX_SKB_FRAGS) { + netdev_err(bn->netdev, + "Agg ring %d: allocated %d/%d buffers (min %d), abort\n", + ring_nr, i, bn->rx_agg_ring_size, MAX_SKB_FRAGS); + goto err_free_one_agg_ring_bufs; + } + + rxr->rx_agg_prod = prod; + + if (i < bn->rx_agg_ring_size) + netdev_warn(bn->netdev, + "Agg ring %d: allocated %d/%d buffers, continuing\n", + ring_nr, i, bn->rx_agg_ring_size); + return 0; + +err_free_one_agg_ring_bufs: + bnge_free_one_agg_ring_bufs(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]; + int rc; + + rc = bnge_alloc_one_rx_ring_bufs(bn, rxr, ring_nr); + if (rc) + return rc; + + if (bnge_is_agg_reqd(bn->bd)) { + rc = bnge_alloc_one_agg_ring_bufs(bn, rxr, ring_nr); + if (rc) + goto err_free_one_rx_ring_bufs; + } + return 0; + +err_free_one_rx_ring_bufs: + bnge_free_one_rx_ring_bufs(bn, rxr); + return rc; +} + +static void bnge_init_rxbd_pages(struct bnge_ring_struct *ring, u32 type) +{ + struct rx_bd **rx_desc_ring; + u32 prod; + int i; + + rx_desc_ring = (struct rx_bd **)ring->ring_mem.pg_arr; + for (i = 0, prod = 0; i < ring->ring_mem.nr_pages; i++) { + struct rx_bd *rxbd = rx_desc_ring[i]; + int j; + + for (j = 0; j < RX_DESC_CNT; j++, rxbd++, prod++) { + rxbd->rx_bd_len_flags_type = cpu_to_le32(type); + rxbd->rx_bd_opaque = prod; + } + } +} + +static void bnge_init_one_rx_ring_rxbd(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + struct bnge_ring_struct *ring; + u32 type; + + type = (bn->rx_buf_use_size << RX_BD_LEN_SHIFT) | + RX_BD_TYPE_RX_PACKET_BD | RX_BD_FLAGS_EOP; + + if (NET_IP_ALIGN == 2) + type |= RX_BD_FLAGS_SOP; + + ring = &rxr->rx_ring_struct; + bnge_init_rxbd_pages(ring, type); + ring->fw_ring_id = INVALID_HW_RING_ID; +} + +static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + struct bnge_ring_struct *ring; + u32 type; + + ring = &rxr->rx_agg_ring_struct; + ring->fw_ring_id = INVALID_HW_RING_ID; + 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; + + bnge_init_rxbd_pages(ring, type); + } +} + +static void bnge_init_one_rx_ring_pair(struct bnge_net *bn, int ring_nr) +{ + struct bnge_rx_ring_info *rxr; + + rxr = &bn->rx_ring[ring_nr]; + bnge_init_one_rx_ring_rxbd(bn, rxr); + + netif_queue_set_napi(bn->netdev, ring_nr, NETDEV_QUEUE_TYPE_RX, + &rxr->bnapi->napi); + + bnge_init_one_agg_ring_rxbd(bn, rxr); +} + +static int bnge_alloc_rx_ring_pair_bufs(struct bnge_net *bn) +{ + int i, rc; + + for (i = 0; i < bn->bd->rx_nr_rings; i++) { + rc = bnge_alloc_one_rx_ring_pair_bufs(bn, i); + if (rc) + goto err_free_rx_ring_pair_bufs; + } + return 0; + +err_free_rx_ring_pair_bufs: + bnge_free_rx_ring_pair_bufs(bn); + return rc; +} + +static void bnge_init_rx_rings(struct bnge_net *bn) +{ + int i; + +#define BNGE_RX_OFFSET (NET_SKB_PAD + NET_IP_ALIGN) +#define BNGE_RX_DMA_OFFSET NET_SKB_PAD + bn->rx_offset = BNGE_RX_OFFSET; + bn->rx_dma_offset = BNGE_RX_DMA_OFFSET; + + for (i = 0; i < bn->bd->rx_nr_rings; i++) + bnge_init_one_rx_ring_pair(bn, i); +} + +static void bnge_init_tx_rings(struct bnge_net *bn) +{ + int i; + + bn->tx_wake_thresh = max(bn->tx_ring_size / 2, BNGE_MIN_TX_DESC_CNT); + + for (i = 0; i < bn->bd->tx_nr_rings; i++) { + 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; + + netif_queue_set_napi(bn->netdev, i, NETDEV_QUEUE_TYPE_TX, + &txr->bnapi->napi); + } +} + +static void bnge_init_vnics(struct bnge_net *bn) +{ + struct bnge_vnic_info *vnic0 = &bn->vnic_info[BNGE_VNIC_DEFAULT]; + int i; + + for (i = 0; i < bn->nr_vnics; i++) { + struct bnge_vnic_info *vnic = &bn->vnic_info[i]; + int j; + + vnic->fw_vnic_id = INVALID_HW_RING_ID; + vnic->vnic_id = i; + for (j = 0; j < BNGE_MAX_CTX_PER_VNIC; j++) + vnic->fw_rss_cos_lb_ctx[j] = INVALID_HW_RING_ID; + + if (bn->vnic_info[i].rss_hash_key) { + if (i == BNGE_VNIC_DEFAULT) { + u8 *key = (void *)vnic->rss_hash_key; + int k; + + if (!bn->rss_hash_key_valid && + !bn->rss_hash_key_updated) { + get_random_bytes(bn->rss_hash_key, + HW_HASH_KEY_SIZE); + bn->rss_hash_key_updated = true; + } + + memcpy(vnic->rss_hash_key, bn->rss_hash_key, + HW_HASH_KEY_SIZE); + + if (!bn->rss_hash_key_updated) + continue; + + bn->rss_hash_key_updated = false; + bn->rss_hash_key_valid = true; + + bn->toeplitz_prefix = 0; + for (k = 0; k < 8; k++) { + bn->toeplitz_prefix <<= 8; + bn->toeplitz_prefix |= key[k]; + } + } else { + memcpy(vnic->rss_hash_key, vnic0->rss_hash_key, + HW_HASH_KEY_SIZE); + } + } + } +} + +static void bnge_set_db_mask(struct bnge_net *bn, struct bnge_db_info *db, + u32 ring_type) +{ + switch (ring_type) { + case HWRM_RING_ALLOC_TX: + db->db_ring_mask = bn->tx_ring_mask; + break; + case HWRM_RING_ALLOC_RX: + db->db_ring_mask = bn->rx_ring_mask; + break; + case HWRM_RING_ALLOC_AGG: + db->db_ring_mask = bn->rx_agg_ring_mask; + break; + case HWRM_RING_ALLOC_CMPL: + case HWRM_RING_ALLOC_NQ: + db->db_ring_mask = bn->cp_ring_mask; + break; + } + db->db_epoch_mask = db->db_ring_mask + 1; + db->db_epoch_shift = DBR_EPOCH_SFT - ilog2(db->db_epoch_mask); +} + +static void bnge_set_db(struct bnge_net *bn, struct bnge_db_info *db, + u32 ring_type, u32 map_idx, u32 xid) +{ + struct bnge_dev *bd = bn->bd; + + switch (ring_type) { + case HWRM_RING_ALLOC_TX: + db->db_key64 = DBR_PATH_L2 | DBR_TYPE_SQ; + break; + case HWRM_RING_ALLOC_RX: + case HWRM_RING_ALLOC_AGG: + db->db_key64 = DBR_PATH_L2 | DBR_TYPE_SRQ; + break; + case HWRM_RING_ALLOC_CMPL: + db->db_key64 = DBR_PATH_L2; + break; + case HWRM_RING_ALLOC_NQ: + db->db_key64 = DBR_PATH_L2; + break; + } + db->db_key64 |= ((u64)xid << DBR_XID_SFT) | DBR_VALID; + + db->doorbell = bd->bar1 + bd->db_offset; + bnge_set_db_mask(bn, db, ring_type); +} + +static int bnge_hwrm_cp_ring_alloc(struct bnge_net *bn, + struct bnge_cp_ring_info *cpr) +{ + const u32 type = HWRM_RING_ALLOC_CMPL; + struct bnge_napi *bnapi = cpr->bnapi; + struct bnge_ring_struct *ring; + u32 map_idx = bnapi->index; + int rc; + + ring = &cpr->ring_struct; + ring->handle = BNGE_SET_NQ_HDL(cpr); + rc = hwrm_ring_alloc_send_msg(bn, ring, type, map_idx); + if (rc) + return rc; + + bnge_set_db(bn, &cpr->cp_db, type, map_idx, ring->fw_ring_id); + bnge_db_cq(bn, &cpr->cp_db, cpr->cp_raw_cons); + + return 0; +} + +static int bnge_hwrm_tx_ring_alloc(struct bnge_net *bn, + struct bnge_tx_ring_info *txr, u32 tx_idx) +{ + struct bnge_ring_struct *ring = &txr->tx_ring_struct; + const u32 type = HWRM_RING_ALLOC_TX; + int rc; + + rc = hwrm_ring_alloc_send_msg(bn, ring, type, tx_idx); + if (rc) + return rc; + + bnge_set_db(bn, &txr->tx_db, type, tx_idx, ring->fw_ring_id); + + return 0; +} + +static int bnge_hwrm_rx_agg_ring_alloc(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + struct bnge_ring_struct *ring = &rxr->rx_agg_ring_struct; + u32 type = HWRM_RING_ALLOC_AGG; + struct bnge_dev *bd = bn->bd; + u32 grp_idx = ring->grp_idx; + u32 map_idx; + int rc; + + map_idx = grp_idx + bd->rx_nr_rings; + rc = hwrm_ring_alloc_send_msg(bn, ring, type, map_idx); + if (rc) + return rc; + + bnge_set_db(bn, &rxr->rx_agg_db, type, map_idx, + 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; + + return 0; +} + +static int bnge_hwrm_rx_ring_alloc(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr) +{ + struct bnge_ring_struct *ring = &rxr->rx_ring_struct; + struct bnge_napi *bnapi = rxr->bnapi; + u32 type = HWRM_RING_ALLOC_RX; + u32 map_idx = bnapi->index; + int rc; + + rc = hwrm_ring_alloc_send_msg(bn, ring, type, map_idx); + if (rc) + 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; + + return 0; +} + +static int bnge_hwrm_ring_alloc(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + bool agg_rings; + int i, rc = 0; + + agg_rings = !!(bnge_is_agg_reqd(bd)); + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + struct bnge_nq_ring_info *nqr = &bnapi->nq_ring; + struct bnge_ring_struct *ring = &nqr->ring_struct; + u32 type = HWRM_RING_ALLOC_NQ; + u32 map_idx = ring->map_idx; + unsigned int vector; + + vector = bd->irq_tbl[map_idx].vector; + disable_irq_nosync(vector); + rc = hwrm_ring_alloc_send_msg(bn, ring, type, map_idx); + if (rc) { + enable_irq(vector); + goto err_out; + } + 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; + + if (!i) { + rc = bnge_hwrm_set_async_event_cr(bd, ring->fw_ring_id); + if (rc) + netdev_warn(bn->netdev, "Failed to set async event completion ring.\n"); + } + } + + for (i = 0; i < bd->tx_nr_rings; i++) { + struct bnge_tx_ring_info *txr = &bn->tx_ring[i]; + + rc = bnge_hwrm_cp_ring_alloc(bn, txr->tx_cpr); + if (rc) + goto err_out; + rc = bnge_hwrm_tx_ring_alloc(bn, txr, i); + if (rc) + goto err_out; + } + + for (i = 0; i < bd->rx_nr_rings; i++) { + struct bnge_rx_ring_info *rxr = &bn->rx_ring[i]; + struct bnge_cp_ring_info *cpr; + struct bnge_ring_struct *ring; + struct bnge_napi *bnapi; + u32 map_idx, type; + + rc = bnge_hwrm_rx_ring_alloc(bn, rxr); + if (rc) + goto err_out; + /* If we have agg rings, post agg buffers first. */ + if (!agg_rings) + bnge_db_write(bn->bd, &rxr->rx_db, rxr->rx_prod); + + cpr = rxr->rx_cpr; + bnapi = rxr->bnapi; + type = HWRM_RING_ALLOC_CMPL; + map_idx = bnapi->index; + + ring = &cpr->ring_struct; + ring->handle = BNGE_SET_NQ_HDL(cpr); + rc = hwrm_ring_alloc_send_msg(bn, ring, type, map_idx); + if (rc) + goto err_out; + bnge_set_db(bn, &cpr->cp_db, type, map_idx, + ring->fw_ring_id); + bnge_db_cq(bn, &cpr->cp_db, cpr->cp_raw_cons); + } + + if (agg_rings) { + for (i = 0; i < bd->rx_nr_rings; i++) { + rc = bnge_hwrm_rx_agg_ring_alloc(bn, &bn->rx_ring[i]); + if (rc) + goto err_out; + } + } +err_out: + return rc; +} + +void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic) +{ + __le16 *ring_tbl = vnic->rss_table; + struct bnge_rx_ring_info *rxr; + struct bnge_dev *bd = bn->bd; + u16 tbl_size, i; + + tbl_size = bnge_get_rxfh_indir_size(bd); + + for (i = 0; i < tbl_size; i++) { + u16 ring_id, 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); + } +} + +static int bnge_hwrm_vnic_rss_cfg(struct bnge_net *bn, + struct bnge_vnic_info *vnic) +{ + int rc; + + rc = bnge_hwrm_vnic_set_rss(bn, vnic, true); + if (rc) { + netdev_err(bn->netdev, "hwrm vnic %d set rss failure rc: %d\n", + vnic->vnic_id, rc); + return rc; + } + rc = bnge_hwrm_vnic_cfg(bn, vnic); + if (rc) + netdev_err(bn->netdev, "hwrm vnic %d cfg failure rc: %d\n", + vnic->vnic_id, rc); + return rc; +} + +static int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic) +{ + struct bnge_dev *bd = bn->bd; + int rc, i, nr_ctxs; + + nr_ctxs = bnge_cal_nr_rss_ctxs(bd->rx_nr_rings); + for (i = 0; i < nr_ctxs; i++) { + rc = bnge_hwrm_vnic_ctx_alloc(bd, vnic, i); + if (rc) { + netdev_err(bn->netdev, "hwrm vnic %d ctx %d alloc failure rc: %d\n", + vnic->vnic_id, i, rc); + return -ENOMEM; + } + bn->rsscos_nr_ctxs++; + } + + rc = bnge_hwrm_vnic_rss_cfg(bn, vnic); + if (rc) + return rc; + + if (bnge_is_agg_reqd(bd)) { + rc = bnge_hwrm_vnic_set_hds(bn, vnic); + if (rc) + netdev_err(bn->netdev, "hwrm vnic %d set hds failure rc: %d\n", + vnic->vnic_id, rc); + } + return rc; +} + +static void bnge_del_l2_filter(struct bnge_net *bn, struct bnge_l2_filter *fltr) +{ + if (!refcount_dec_and_test(&fltr->refcnt)) + return; + hlist_del_rcu(&fltr->base.hash); + kfree_rcu(fltr, base.rcu); +} + +static void bnge_init_l2_filter(struct bnge_net *bn, + struct bnge_l2_filter *fltr, + struct bnge_l2_key *key, u32 idx) +{ + struct hlist_head *head; + + ether_addr_copy(fltr->l2_key.dst_mac_addr, key->dst_mac_addr); + fltr->l2_key.vlan = key->vlan; + fltr->base.type = BNGE_FLTR_TYPE_L2; + + head = &bn->l2_fltr_hash_tbl[idx]; + hlist_add_head_rcu(&fltr->base.hash, head); + refcount_set(&fltr->refcnt, 1); +} + +static struct bnge_l2_filter *__bnge_lookup_l2_filter(struct bnge_net *bn, + struct bnge_l2_key *key, + u32 idx) +{ + struct bnge_l2_filter *fltr; + struct hlist_head *head; + + head = &bn->l2_fltr_hash_tbl[idx]; + hlist_for_each_entry_rcu(fltr, head, base.hash) { + struct bnge_l2_key *l2_key = &fltr->l2_key; + + if (ether_addr_equal(l2_key->dst_mac_addr, key->dst_mac_addr) && + l2_key->vlan == key->vlan) + return fltr; + } + return NULL; +} + +static struct bnge_l2_filter *bnge_lookup_l2_filter(struct bnge_net *bn, + struct bnge_l2_key *key, + u32 idx) +{ + struct bnge_l2_filter *fltr; + + rcu_read_lock(); + fltr = __bnge_lookup_l2_filter(bn, key, idx); + if (fltr) + refcount_inc(&fltr->refcnt); + rcu_read_unlock(); + return fltr; +} + +static struct bnge_l2_filter *bnge_alloc_l2_filter(struct bnge_net *bn, + struct bnge_l2_key *key, + gfp_t gfp) +{ + struct bnge_l2_filter *fltr; + u32 idx; + + idx = jhash2(&key->filter_key, BNGE_L2_KEY_SIZE, bn->hash_seed) & + BNGE_L2_FLTR_HASH_MASK; + fltr = bnge_lookup_l2_filter(bn, key, idx); + if (fltr) + return fltr; + + fltr = kzalloc(sizeof(*fltr), gfp); + if (!fltr) + return ERR_PTR(-ENOMEM); + + bnge_init_l2_filter(bn, fltr, key, idx); + return fltr; +} + +static int bnge_hwrm_set_vnic_filter(struct bnge_net *bn, u16 vnic_id, u16 idx, + const u8 *mac_addr) +{ + struct bnge_l2_filter *fltr; + struct bnge_l2_key key; + int rc; + + ether_addr_copy(key.dst_mac_addr, mac_addr); + key.vlan = 0; + fltr = bnge_alloc_l2_filter(bn, &key, GFP_KERNEL); + if (IS_ERR(fltr)) + return PTR_ERR(fltr); + + fltr->base.fw_vnic_id = bn->vnic_info[vnic_id].fw_vnic_id; + rc = bnge_hwrm_l2_filter_alloc(bn->bd, fltr); + if (rc) + goto err_del_l2_filter; + bn->vnic_info[vnic_id].l2_filters[idx] = fltr; + return rc; + +err_del_l2_filter: + bnge_del_l2_filter(bn, fltr); + return rc; +} + +static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask) +{ + 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) { + if (mc_count >= BNGE_MAX_MC_ADDRS) { + *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; + vnic->mc_list_count = 0; + return false; + } + haddr = ha->addr; + if (!ether_addr_equal(haddr, vnic->mc_list + off)) { + memcpy(vnic->mc_list + off, haddr, ETH_ALEN); + update = true; + } + off += ETH_ALEN; + mc_count++; + } + if (mc_count) + *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_MCAST; + + if (mc_count != vnic->mc_list_count) { + vnic->mc_list_count = mc_count; + update = true; + } + return update; +} + +static bool bnge_uc_list_updated(struct bnge_net *bn) +{ + 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)) + return true; + + netdev_for_each_uc_addr(ha, dev) { + if (!ether_addr_equal(ha->addr, vnic->uc_list + off)) + return true; + + off += ETH_ALEN; + } + return false; +} + +static bool bnge_promisc_ok(struct bnge_net *bn) +{ + return true; +} + +static int bnge_cfg_def_vnic(struct bnge_net *bn) +{ + 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; + + for (i = 1; i < vnic->uc_filter_count; i++) { + struct bnge_l2_filter *fltr = vnic->l2_filters[i]; + + bnge_hwrm_l2_filter_free(bd, fltr); + bnge_del_l2_filter(bn, fltr); + } + + vnic->uc_filter_count = 1; + + netif_addr_lock_bh(dev); + if (netdev_uc_count(dev) > (BNGE_MAX_UC_ADDRS - 1)) { + vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; + } else { + netdev_for_each_uc_addr(ha, dev) { + memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN); + off += ETH_ALEN; + vnic->uc_filter_count++; + } + } + 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); + vnic->uc_filter_count = i; + return rc; + } + } + +skip_uc: + if ((vnic->rx_mask & CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS) && + !bnge_promisc_ok(bn)) + vnic->rx_mask &= ~CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; + rc = bnge_hwrm_cfa_l2_set_rx_mask(bd, vnic); + if (rc && (vnic->rx_mask & CFA_L2_SET_RX_MASK_REQ_MASK_MCAST)) { + netdev_info(dev, "Failed setting MC filters rc: %d, turning on ALL_MCAST mode\n", + rc); + vnic->rx_mask &= ~CFA_L2_SET_RX_MASK_REQ_MASK_MCAST; + vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; + 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); + + return rc; +} + +static void bnge_hwrm_vnic_free(struct bnge_net *bn) +{ + int i; + + for (i = 0; i < bn->nr_vnics; i++) + bnge_hwrm_vnic_free_one(bn->bd, &bn->vnic_info[i]); +} + +static void bnge_hwrm_vnic_ctx_free(struct bnge_net *bn) +{ + int i, j; + + for (i = 0; i < bn->nr_vnics; i++) { + struct bnge_vnic_info *vnic = &bn->vnic_info[i]; + + for (j = 0; j < BNGE_MAX_CTX_PER_VNIC; j++) { + if (vnic->fw_rss_cos_lb_ctx[j] != INVALID_HW_RING_ID) + bnge_hwrm_vnic_ctx_free_one(bn->bd, vnic, j); + } + } + bn->rsscos_nr_ctxs = 0; +} + +static void bnge_hwrm_clear_vnic_filter(struct bnge_net *bn) +{ + struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; + int i; + + for (i = 0; i < vnic->uc_filter_count; i++) { + struct bnge_l2_filter *fltr = vnic->l2_filters[i]; + + bnge_hwrm_l2_filter_free(bn->bd, fltr); + bnge_del_l2_filter(bn, fltr); + } + + vnic->uc_filter_count = 0; +} + +static void bnge_clear_vnic(struct bnge_net *bn) +{ + bnge_hwrm_clear_vnic_filter(bn); + bnge_hwrm_vnic_free(bn); + bnge_hwrm_vnic_ctx_free(bn); +} + +static void bnge_hwrm_rx_ring_free(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr, + bool close_path) +{ + struct bnge_ring_struct *ring = &rxr->rx_ring_struct; + u32 grp_idx = rxr->bnapi->index; + u32 cmpl_ring_id; + + if (ring->fw_ring_id == INVALID_HW_RING_ID) + return; + + cmpl_ring_id = bnge_cp_ring_for_rx(rxr); + hwrm_ring_free_send_msg(bn, ring, + RING_FREE_REQ_RING_TYPE_RX, + close_path ? cmpl_ring_id : + INVALID_HW_RING_ID); + ring->fw_ring_id = INVALID_HW_RING_ID; + bn->grp_info[grp_idx].rx_fw_ring_id = INVALID_HW_RING_ID; +} + +static void bnge_hwrm_rx_agg_ring_free(struct bnge_net *bn, + struct bnge_rx_ring_info *rxr, + bool close_path) +{ + struct bnge_ring_struct *ring = &rxr->rx_agg_ring_struct; + u32 grp_idx = rxr->bnapi->index; + u32 cmpl_ring_id; + + if (ring->fw_ring_id == INVALID_HW_RING_ID) + 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; + bn->grp_info[grp_idx].agg_fw_ring_id = INVALID_HW_RING_ID; +} + +static void bnge_hwrm_tx_ring_free(struct bnge_net *bn, + struct bnge_tx_ring_info *txr, + bool close_path) +{ + struct bnge_ring_struct *ring = &txr->tx_ring_struct; + u32 cmpl_ring_id; + + if (ring->fw_ring_id == INVALID_HW_RING_ID) + 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; +} + +static void bnge_hwrm_cp_ring_free(struct bnge_net *bn, + struct bnge_cp_ring_info *cpr) +{ + struct bnge_ring_struct *ring; + + ring = &cpr->ring_struct; + if (ring->fw_ring_id == INVALID_HW_RING_ID) + 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; +} + +static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path) +{ + struct bnge_dev *bd = bn->bd; + int i; + + if (!bn->bnapi) + return; + + for (i = 0; i < bd->tx_nr_rings; i++) + bnge_hwrm_tx_ring_free(bn, &bn->tx_ring[i], close_path); + + for (i = 0; i < bd->rx_nr_rings; i++) { + bnge_hwrm_rx_ring_free(bn, &bn->rx_ring[i], close_path); + bnge_hwrm_rx_agg_ring_free(bn, &bn->rx_ring[i], close_path); + } + + 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; + int j; + + nqr = &bnapi->nq_ring; + for (j = 0; j < nqr->cp_ring_count && nqr->cp_ring_arr; j++) + bnge_hwrm_cp_ring_free(bn, &nqr->cp_ring_arr[j]); + + ring = &nqr->ring_struct; + if (ring->fw_ring_id != INVALID_HW_RING_ID) { + 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; + bn->grp_info[i].nq_fw_ring_id = INVALID_HW_RING_ID; + } + } +} + +static void bnge_setup_msix(struct bnge_net *bn) +{ + struct net_device *dev = bn->netdev; + struct bnge_dev *bd = bn->bd; + int len, i; + + len = sizeof(bd->irq_tbl[0].name); + for (i = 0; i < bd->nq_nr_rings; i++) { + int map_idx = bnge_cp_num_to_irq_num(bn, i); + char *attr; + + if (bd->flags & BNGE_EN_SHARED_CHNL) + attr = "TxRx"; + else if (i < bd->rx_nr_rings) + attr = "rx"; + else + attr = "tx"; + + snprintf(bd->irq_tbl[map_idx].name, len, "%s-%s-%d", dev->name, + attr, i); + bd->irq_tbl[map_idx].handler = bnge_msix; + } +} + +static int bnge_setup_interrupts(struct bnge_net *bn) +{ + struct net_device *dev = bn->netdev; + struct bnge_dev *bd = bn->bd; + + bnge_setup_msix(bn); + + return netif_set_real_num_queues(dev, bd->tx_nr_rings, bd->rx_nr_rings); +} + +static void bnge_hwrm_resource_free(struct bnge_net *bn, bool close_path) +{ + bnge_clear_vnic(bn); + bnge_hwrm_ring_free(bn, close_path); + bnge_hwrm_stat_ctx_free(bn); +} + +static void bnge_free_irq(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + struct bnge_irq *irq; + int i; + + for (i = 0; i < bd->nq_nr_rings; i++) { + int map_idx = bnge_cp_num_to_irq_num(bn, i); + + irq = &bd->irq_tbl[map_idx]; + if (irq->requested) { + if (irq->have_cpumask) { + irq_set_affinity_hint(irq->vector, NULL); + free_cpumask_var(irq->cpu_mask); + irq->have_cpumask = 0; + } + free_irq(irq->vector, bn->bnapi[i]); + } + + irq->requested = 0; + } +} + +static int bnge_request_irq(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i, rc; + + rc = bnge_setup_interrupts(bn); + if (rc) { + netdev_err(bn->netdev, "bnge_setup_interrupts err: %d\n", rc); + return rc; + } + for (i = 0; i < bd->nq_nr_rings; i++) { + int map_idx = bnge_cp_num_to_irq_num(bn, i); + struct bnge_irq *irq = &bd->irq_tbl[map_idx]; + + rc = request_irq(irq->vector, irq->handler, 0, irq->name, + bn->bnapi[i]); + if (rc) + goto err_free_irq; + + netif_napi_set_irq_locked(&bn->bnapi[i]->napi, irq->vector); + irq->requested = 1; + + if (zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) { + int numa_node = dev_to_node(&bd->pdev->dev); + + irq->have_cpumask = 1; + cpumask_set_cpu(cpumask_local_spread(i, numa_node), + irq->cpu_mask); + rc = irq_set_affinity_hint(irq->vector, irq->cpu_mask); + if (rc) { + netdev_warn(bn->netdev, + "Set affinity failed, IRQ = %d\n", + irq->vector); + goto err_free_irq; + } + } + } + return 0; + +err_free_irq: + bnge_free_irq(bn); + return rc; +} + +static int bnge_init_chip(struct bnge_net *bn) +{ + struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT]; + struct bnge_dev *bd = bn->bd; + int rc; + +#define BNGE_DEF_STATS_COAL_TICKS 1000000 + bn->stats_coal_ticks = BNGE_DEF_STATS_COAL_TICKS; + + rc = bnge_hwrm_stat_ctx_alloc(bn); + if (rc) { + netdev_err(bn->netdev, "hwrm stat ctx alloc failure rc: %d\n", rc); + goto err_out; + } + + rc = bnge_hwrm_ring_alloc(bn); + if (rc) { + netdev_err(bn->netdev, "hwrm ring alloc failure rc: %d\n", rc); + goto err_out; + } + + rc = bnge_hwrm_vnic_alloc(bd, vnic, bd->rx_nr_rings); + if (rc) { + netdev_err(bn->netdev, "hwrm vnic alloc failure rc: %d\n", rc); + goto err_out; + } + + rc = bnge_setup_vnic(bn, vnic); + if (rc) + goto err_out; + + if (bd->rss_cap & BNGE_RSS_CAP_RSS_HASH_TYPE_DELTA) + bnge_hwrm_update_rss_hash_cfg(bn); + + /* Filter for default vnic 0 */ + rc = bnge_hwrm_set_vnic_filter(bn, 0, 0, bn->netdev->dev_addr); + if (rc) { + netdev_err(bn->netdev, "HWRM vnic filter failure rc: %d\n", rc); + goto err_out; + } + vnic->uc_filter_count = 1; + + vnic->rx_mask = 0; + + if (bn->netdev->flags & IFF_BROADCAST) + vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; + + if (bn->netdev->flags & IFF_PROMISC) + vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; + + if (bn->netdev->flags & IFF_ALLMULTI) { + vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; + vnic->mc_list_count = 0; + } else if (bn->netdev->flags & IFF_MULTICAST) { + u32 mask = 0; + + bnge_mc_list_updated(bn, &mask); + vnic->rx_mask |= mask; + } + + rc = bnge_cfg_def_vnic(bn); + if (rc) + goto err_out; + return 0; + +err_out: + bnge_hwrm_resource_free(bn, 0); + 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; + struct bnge_napi *bnapi; + int i; + + for (i = 0; i < bd->nq_nr_rings; i++) { + bnapi = bn->bnapi[i]; + netif_napi_add_config_locked(bn->netdev, &bnapi->napi, + bnge_napi_poll, bnapi->index); + } +} + +static void bnge_del_napi(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int i; + + for (i = 0; i < bd->rx_nr_rings; i++) + netif_queue_set_napi(bn->netdev, i, NETDEV_QUEUE_TYPE_RX, NULL); + for (i = 0; i < bd->tx_nr_rings; i++) + netif_queue_set_napi(bn->netdev, i, NETDEV_QUEUE_TYPE_TX, NULL); + + for (i = 0; i < bd->nq_nr_rings; i++) { + struct bnge_napi *bnapi = bn->bnapi[i]; + + __netif_napi_del_locked(&bnapi->napi); + } + + /* Wait for RCU grace period after removing NAPI instances */ + synchronize_net(); +} + +static int bnge_init_nic(struct bnge_net *bn) +{ + int rc; + + bnge_init_nq_tree(bn); + + bnge_init_rx_rings(bn); + rc = bnge_alloc_rx_ring_pair_bufs(bn); + if (rc) + return rc; + + bnge_init_tx_rings(bn); + + rc = bnge_init_ring_grps(bn); + if (rc) + goto err_free_rx_ring_pair_bufs; + + bnge_init_vnics(bn); + + rc = bnge_init_chip(bn); + if (rc) + goto err_free_ring_grps; + return rc; + +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 int bnge_open_core(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + int rc; + + netif_carrier_off(bn->netdev); + + rc = bnge_reserve_rings(bd); + if (rc) { + netdev_err(bn->netdev, "bnge_reserve_rings err: %d\n", rc); + return rc; + } + + rc = bnge_alloc_core(bn); + if (rc) { + netdev_err(bn->netdev, "bnge_alloc_core err: %d\n", rc); + return rc; + } + + bnge_init_napi(bn); + rc = bnge_request_irq(bn); + if (rc) { + netdev_err(bn->netdev, "bnge_request_irq err: %d\n", rc); + goto err_del_napi; + } + + rc = bnge_init_nic(bn); + if (rc) { + netdev_err(bn->netdev, "bnge_init_nic err: %d\n", rc); + goto err_free_irq; + } + set_bit(BNGE_STATE_OPEN, &bd->state); + return 0; + +err_free_irq: + bnge_free_irq(bn); +err_del_napi: + bnge_del_napi(bn); + bnge_free_core(bn); + return rc; +} static netdev_tx_t bnge_start_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -28,11 +2212,42 @@ static netdev_tx_t bnge_start_xmit(struct sk_buff *skb, struct net_device *dev) static int bnge_open(struct net_device *dev) { + struct bnge_net *bn = netdev_priv(dev); + int rc; + + rc = bnge_open_core(bn); + if (rc) + netdev_err(dev, "bnge_open_core err: %d\n", rc); + + return rc; +} + +static int bnge_shutdown_nic(struct bnge_net *bn) +{ + /* TODO: close_path = 0 until we make NAPI functional */ + bnge_hwrm_resource_free(bn, 0); return 0; } +static void bnge_close_core(struct bnge_net *bn) +{ + struct bnge_dev *bd = bn->bd; + + clear_bit(BNGE_STATE_OPEN, &bd->state); + bnge_shutdown_nic(bn); + bnge_free_all_rings_bufs(bn); + bnge_free_irq(bn); + bnge_del_napi(bn); + + bnge_free_core(bn); +} + static int bnge_close(struct net_device *dev) { + struct bnge_net *bn = netdev_priv(dev); + + bnge_close_core(bn); + return 0; } @@ -238,6 +2453,7 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs) 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_set_ring_params(bd); @@ -245,6 +2461,7 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs) bnge_init_l2_fltr_tbl(bn); bnge_init_mac_addr(bd); + netdev->request_ops_lock = true; rc = register_netdev(netdev); if (rc) { dev_err(bd->dev, "Register netdev failed rc: %d\n", rc); |