// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2025 Broadcom. #include #include #include #include #include #include #include #include #include #include "bnge.h" #include "bnge_hwrm_lib.h" #include "bnge_rmem.h" static void bnge_init_ctx_mem(struct bnge_ctx_mem_type *ctxm, void *p, int len) { u8 init_val = ctxm->init_value; u16 offset = ctxm->init_offset; u8 *p2 = p; int i; if (!init_val) return; if (offset == BNGE_CTX_INIT_INVALID_OFFSET) { memset(p, init_val, len); return; } for (i = 0; i < len; i += ctxm->entry_size) *(p2 + i + offset) = init_val; } void bnge_free_ring(struct bnge_dev *bd, struct bnge_ring_mem_info *rmem) { struct pci_dev *pdev = bd->pdev; int i; if (!rmem->pg_arr) goto skip_pages; for (i = 0; i < rmem->nr_pages; i++) { if (!rmem->pg_arr[i]) continue; dma_free_coherent(&pdev->dev, rmem->page_size, rmem->pg_arr[i], rmem->dma_arr[i]); rmem->pg_arr[i] = NULL; } skip_pages: if (rmem->pg_tbl) { size_t pg_tbl_size = rmem->nr_pages * 8; if (rmem->flags & BNGE_RMEM_USE_FULL_PAGE_FLAG) pg_tbl_size = rmem->page_size; dma_free_coherent(&pdev->dev, pg_tbl_size, rmem->pg_tbl, rmem->dma_pg_tbl); rmem->pg_tbl = NULL; } if (rmem->vmem_size && *rmem->vmem) { vfree(*rmem->vmem); *rmem->vmem = NULL; } } int bnge_alloc_ring(struct bnge_dev *bd, struct bnge_ring_mem_info *rmem) { struct pci_dev *pdev = bd->pdev; u64 valid_bit = 0; int i; if (rmem->flags & (BNGE_RMEM_VALID_PTE_FLAG | BNGE_RMEM_RING_PTE_FLAG)) valid_bit = PTU_PTE_VALID; if ((rmem->nr_pages > 1 || rmem->depth > 0) && !rmem->pg_tbl) { size_t pg_tbl_size = rmem->nr_pages * 8; if (rmem->flags & BNGE_RMEM_USE_FULL_PAGE_FLAG) pg_tbl_size = rmem->page_size; rmem->pg_tbl = dma_alloc_coherent(&pdev->dev, pg_tbl_size, &rmem->dma_pg_tbl, GFP_KERNEL); if (!rmem->pg_tbl) return -ENOMEM; } for (i = 0; i < rmem->nr_pages; i++) { u64 extra_bits = valid_bit; rmem->pg_arr[i] = dma_alloc_coherent(&pdev->dev, rmem->page_size, &rmem->dma_arr[i], GFP_KERNEL); if (!rmem->pg_arr[i]) return -ENOMEM; if (rmem->ctx_mem) bnge_init_ctx_mem(rmem->ctx_mem, rmem->pg_arr[i], rmem->page_size); if (rmem->nr_pages > 1 || rmem->depth > 0) { if (i == rmem->nr_pages - 2 && (rmem->flags & BNGE_RMEM_RING_PTE_FLAG)) extra_bits |= PTU_PTE_NEXT_TO_LAST; else if (i == rmem->nr_pages - 1 && (rmem->flags & BNGE_RMEM_RING_PTE_FLAG)) extra_bits |= PTU_PTE_LAST; rmem->pg_tbl[i] = cpu_to_le64(rmem->dma_arr[i] | extra_bits); } } if (rmem->vmem_size) { *rmem->vmem = vzalloc(rmem->vmem_size); if (!(*rmem->vmem)) return -ENOMEM; } return 0; } static int bnge_alloc_ctx_one_lvl(struct bnge_dev *bd, struct bnge_ctx_pg_info *ctx_pg) { struct bnge_ring_mem_info *rmem = &ctx_pg->ring_mem; rmem->page_size = BNGE_PAGE_SIZE; rmem->pg_arr = ctx_pg->ctx_pg_arr; rmem->dma_arr = ctx_pg->ctx_dma_arr; rmem->flags = BNGE_RMEM_VALID_PTE_FLAG; if (rmem->depth >= 1) rmem->flags |= BNGE_RMEM_USE_FULL_PAGE_FLAG; return bnge_alloc_ring(bd, rmem); } static int bnge_alloc_ctx_pg_tbls(struct bnge_dev *bd, struct bnge_ctx_pg_info *ctx_pg, u32 mem_size, u8 depth, struct bnge_ctx_mem_type *ctxm) { struct bnge_ring_mem_info *rmem = &ctx_pg->ring_mem; int rc; if (!mem_size) return -EINVAL; ctx_pg->nr_pages = DIV_ROUND_UP(mem_size, BNGE_PAGE_SIZE); if (ctx_pg->nr_pages > MAX_CTX_TOTAL_PAGES) { ctx_pg->nr_pages = 0; return -EINVAL; } if (ctx_pg->nr_pages > MAX_CTX_PAGES || depth > 1) { int nr_tbls, i; rmem->depth = 2; ctx_pg->ctx_pg_tbl = kcalloc(MAX_CTX_PAGES, sizeof(ctx_pg), GFP_KERNEL); if (!ctx_pg->ctx_pg_tbl) return -ENOMEM; nr_tbls = DIV_ROUND_UP(ctx_pg->nr_pages, MAX_CTX_PAGES); rmem->nr_pages = nr_tbls; rc = bnge_alloc_ctx_one_lvl(bd, ctx_pg); if (rc) return rc; for (i = 0; i < nr_tbls; i++) { struct bnge_ctx_pg_info *pg_tbl; pg_tbl = kzalloc(sizeof(*pg_tbl), GFP_KERNEL); if (!pg_tbl) return -ENOMEM; ctx_pg->ctx_pg_tbl[i] = pg_tbl; rmem = &pg_tbl->ring_mem; rmem->pg_tbl = ctx_pg->ctx_pg_arr[i]; rmem->dma_pg_tbl = ctx_pg->ctx_dma_arr[i]; rmem->depth = 1; rmem->nr_pages = MAX_CTX_PAGES; rmem->ctx_mem = ctxm; if (i == (nr_tbls - 1)) { int rem = ctx_pg->nr_pages % MAX_CTX_PAGES; if (rem) rmem->nr_pages = rem; } rc = bnge_alloc_ctx_one_lvl(bd, pg_tbl); if (rc) break; } } else { rmem->nr_pages = DIV_ROUND_UP(mem_size, BNGE_PAGE_SIZE); if (rmem->nr_pages > 1 || depth) rmem->depth = 1; rmem->ctx_mem = ctxm; rc = bnge_alloc_ctx_one_lvl(bd, ctx_pg); } return rc; } static void bnge_free_ctx_pg_tbls(struct bnge_dev *bd, struct bnge_ctx_pg_info *ctx_pg) { struct bnge_ring_mem_info *rmem = &ctx_pg->ring_mem; if (rmem->depth > 1 || ctx_pg->nr_pages > MAX_CTX_PAGES || ctx_pg->ctx_pg_tbl) { int i, nr_tbls = rmem->nr_pages; for (i = 0; i < nr_tbls; i++) { struct bnge_ctx_pg_info *pg_tbl; struct bnge_ring_mem_info *rmem2; pg_tbl = ctx_pg->ctx_pg_tbl[i]; if (!pg_tbl) continue; rmem2 = &pg_tbl->ring_mem; bnge_free_ring(bd, rmem2); ctx_pg->ctx_pg_arr[i] = NULL; kfree(pg_tbl); ctx_pg->ctx_pg_tbl[i] = NULL; } kfree(ctx_pg->ctx_pg_tbl); ctx_pg->ctx_pg_tbl = NULL; } bnge_free_ring(bd, rmem); ctx_pg->nr_pages = 0; } static int bnge_setup_ctxm_pg_tbls(struct bnge_dev *bd, struct bnge_ctx_mem_type *ctxm, u32 entries, u8 pg_lvl) { struct bnge_ctx_pg_info *ctx_pg = ctxm->pg_info; int i, rc = 0, n = 1; u32 mem_size; if (!ctxm->entry_size || !ctx_pg) return -EINVAL; if (ctxm->instance_bmap) n = hweight32(ctxm->instance_bmap); if (ctxm->entry_multiple) entries = roundup(entries, ctxm->entry_multiple); entries = clamp_t(u32, entries, ctxm->min_entries, ctxm->max_entries); mem_size = entries * ctxm->entry_size; for (i = 0; i < n && !rc; i++) { ctx_pg[i].entries = entries; rc = bnge_alloc_ctx_pg_tbls(bd, &ctx_pg[i], mem_size, pg_lvl, ctxm->init_value ? ctxm : NULL); } return rc; } static int bnge_backing_store_cfg(struct bnge_dev *bd, u32 ena) { struct bnge_ctx_mem_info *ctx = bd->ctx; struct bnge_ctx_mem_type *ctxm; u16 last_type; int rc = 0; u16 type; if (!ena) return 0; else if (ena & FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM) last_type = BNGE_CTX_MAX - 1; else last_type = BNGE_CTX_L2_MAX - 1; ctx->ctx_arr[last_type].last = 1; for (type = 0 ; type < BNGE_CTX_V2_MAX; type++) { ctxm = &ctx->ctx_arr[type]; rc = bnge_hwrm_func_backing_store(bd, ctxm, ctxm->last); if (rc) return rc; } return 0; } void bnge_free_ctx_mem(struct bnge_dev *bd) { struct bnge_ctx_mem_info *ctx = bd->ctx; u16 type; if (!ctx) return; for (type = 0; type < BNGE_CTX_V2_MAX; type++) { struct bnge_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; struct bnge_ctx_pg_info *ctx_pg = ctxm->pg_info; int i, n = 1; if (!ctx_pg) continue; if (ctxm->instance_bmap) n = hweight32(ctxm->instance_bmap); for (i = 0; i < n; i++) bnge_free_ctx_pg_tbls(bd, &ctx_pg[i]); kfree(ctx_pg); ctxm->pg_info = NULL; } ctx->flags &= ~BNGE_CTX_FLAG_INITED; kfree(ctx); bd->ctx = NULL; } #define FUNC_BACKING_STORE_CFG_REQ_DFLT_ENABLES \ (FUNC_BACKING_STORE_CFG_REQ_ENABLES_QP | \ FUNC_BACKING_STORE_CFG_REQ_ENABLES_SRQ | \ FUNC_BACKING_STORE_CFG_REQ_ENABLES_CQ | \ FUNC_BACKING_STORE_CFG_REQ_ENABLES_VNIC | \ FUNC_BACKING_STORE_CFG_REQ_ENABLES_STAT) int bnge_alloc_ctx_mem(struct bnge_dev *bd) { struct bnge_ctx_mem_type *ctxm; struct bnge_ctx_mem_info *ctx; u32 l2_qps, qp1_qps, max_qps; u32 ena, entries_sp, entries; u32 srqs, max_srqs, min; u32 num_mr, num_ah; u32 extra_srqs = 0; u32 extra_qps = 0; u32 fast_qpmd_qps; u8 pg_lvl = 1; int i, rc; rc = bnge_hwrm_func_backing_store_qcaps(bd); if (rc) { dev_err(bd->dev, "Failed querying ctx mem caps, rc: %d\n", rc); return rc; } ctx = bd->ctx; if (!ctx || (ctx->flags & BNGE_CTX_FLAG_INITED)) return 0; ctxm = &ctx->ctx_arr[BNGE_CTX_QP]; l2_qps = ctxm->qp_l2_entries; qp1_qps = ctxm->qp_qp1_entries; fast_qpmd_qps = ctxm->qp_fast_qpmd_entries; max_qps = ctxm->max_entries; ctxm = &ctx->ctx_arr[BNGE_CTX_SRQ]; srqs = ctxm->srq_l2_entries; max_srqs = ctxm->max_entries; ena = 0; if (bnge_is_roce_en(bd) && !is_kdump_kernel()) { pg_lvl = 2; extra_qps = min_t(u32, 65536, max_qps - l2_qps - qp1_qps); /* allocate extra qps if fast qp destroy feature enabled */ extra_qps += fast_qpmd_qps; extra_srqs = min_t(u32, 8192, max_srqs - srqs); if (fast_qpmd_qps) ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_QP_FAST_QPMD; } ctxm = &ctx->ctx_arr[BNGE_CTX_QP]; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, l2_qps + qp1_qps + extra_qps, pg_lvl); if (rc) return rc; ctxm = &ctx->ctx_arr[BNGE_CTX_SRQ]; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, srqs + extra_srqs, pg_lvl); if (rc) return rc; ctxm = &ctx->ctx_arr[BNGE_CTX_CQ]; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, ctxm->cq_l2_entries + extra_qps * 2, pg_lvl); if (rc) return rc; ctxm = &ctx->ctx_arr[BNGE_CTX_VNIC]; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, ctxm->max_entries, 1); if (rc) return rc; ctxm = &ctx->ctx_arr[BNGE_CTX_STAT]; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, ctxm->max_entries, 1); if (rc) return rc; if (!bnge_is_roce_en(bd)) goto skip_rdma; ctxm = &ctx->ctx_arr[BNGE_CTX_MRAV]; /* 128K extra is needed to accommodate static AH context * allocation by f/w. */ num_mr = min_t(u32, ctxm->max_entries / 2, 1024 * 256); num_ah = min_t(u32, num_mr, 1024 * 128); ctxm->split_entry_cnt = BNGE_CTX_MRAV_AV_SPLIT_ENTRY + 1; if (!ctxm->mrav_av_entries || ctxm->mrav_av_entries > num_ah) ctxm->mrav_av_entries = num_ah; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, num_mr + num_ah, 2); if (rc) return rc; ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV; ctxm = &ctx->ctx_arr[BNGE_CTX_TIM]; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, l2_qps + qp1_qps + extra_qps, 1); if (rc) return rc; ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM; skip_rdma: ctxm = &ctx->ctx_arr[BNGE_CTX_STQM]; min = ctxm->min_entries; entries_sp = ctx->ctx_arr[BNGE_CTX_VNIC].vnic_entries + l2_qps + 2 * (extra_qps + qp1_qps) + min; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, entries_sp, 2); if (rc) return rc; ctxm = &ctx->ctx_arr[BNGE_CTX_FTQM]; entries = l2_qps + 2 * (extra_qps + qp1_qps); rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, entries, 2); if (rc) return rc; for (i = 0; i < ctx->tqm_fp_rings_count + 1; i++) ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_SP << i; ena |= FUNC_BACKING_STORE_CFG_REQ_DFLT_ENABLES; rc = bnge_backing_store_cfg(bd, ena); if (rc) { dev_err(bd->dev, "Failed configuring ctx mem, rc: %d\n", rc); return rc; } ctx->flags |= BNGE_CTX_FLAG_INITED; return 0; }