mirror of https://github.com/torvalds/linux.git
280 lines
6.8 KiB
C
280 lines
6.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2025 Broadcom.
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <rdma/ib_umem.h>
|
|
|
|
#include <linux/bnxt/hsi.h>
|
|
#include "bng_res.h"
|
|
#include "roce_hsi.h"
|
|
|
|
/* Stats */
|
|
void bng_re_free_stats_ctx_mem(struct pci_dev *pdev,
|
|
struct bng_re_stats *stats)
|
|
{
|
|
if (stats->dma) {
|
|
dma_free_coherent(&pdev->dev, stats->size,
|
|
stats->dma, stats->dma_map);
|
|
}
|
|
memset(stats, 0, sizeof(*stats));
|
|
stats->fw_id = -1;
|
|
}
|
|
|
|
int bng_re_alloc_stats_ctx_mem(struct pci_dev *pdev,
|
|
struct bng_re_chip_ctx *cctx,
|
|
struct bng_re_stats *stats)
|
|
{
|
|
memset(stats, 0, sizeof(*stats));
|
|
stats->fw_id = -1;
|
|
stats->size = cctx->hw_stats_size;
|
|
stats->dma = dma_alloc_coherent(&pdev->dev, stats->size,
|
|
&stats->dma_map, GFP_KERNEL);
|
|
if (!stats->dma)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bng_free_pbl(struct bng_re_res *res, struct bng_re_pbl *pbl)
|
|
{
|
|
struct pci_dev *pdev = res->pdev;
|
|
int i;
|
|
|
|
for (i = 0; i < pbl->pg_count; i++) {
|
|
if (pbl->pg_arr[i])
|
|
dma_free_coherent(&pdev->dev, pbl->pg_size,
|
|
(void *)((unsigned long)
|
|
pbl->pg_arr[i] &
|
|
PAGE_MASK),
|
|
pbl->pg_map_arr[i]);
|
|
else
|
|
dev_warn(&pdev->dev,
|
|
"PBL free pg_arr[%d] empty?!\n", i);
|
|
pbl->pg_arr[i] = NULL;
|
|
}
|
|
|
|
vfree(pbl->pg_arr);
|
|
pbl->pg_arr = NULL;
|
|
vfree(pbl->pg_map_arr);
|
|
pbl->pg_map_arr = NULL;
|
|
pbl->pg_count = 0;
|
|
pbl->pg_size = 0;
|
|
}
|
|
|
|
static int bng_alloc_pbl(struct bng_re_res *res,
|
|
struct bng_re_pbl *pbl,
|
|
struct bng_re_sg_info *sginfo)
|
|
{
|
|
struct pci_dev *pdev = res->pdev;
|
|
u32 pages;
|
|
int i;
|
|
|
|
if (sginfo->nopte)
|
|
return 0;
|
|
pages = sginfo->npages;
|
|
|
|
/* page ptr arrays */
|
|
pbl->pg_arr = vmalloc_array(pages, sizeof(void *));
|
|
if (!pbl->pg_arr)
|
|
return -ENOMEM;
|
|
|
|
pbl->pg_map_arr = vmalloc_array(pages, sizeof(dma_addr_t));
|
|
if (!pbl->pg_map_arr) {
|
|
vfree(pbl->pg_arr);
|
|
pbl->pg_arr = NULL;
|
|
return -ENOMEM;
|
|
}
|
|
pbl->pg_count = 0;
|
|
pbl->pg_size = sginfo->pgsize;
|
|
|
|
for (i = 0; i < pages; i++) {
|
|
pbl->pg_arr[i] = dma_alloc_coherent(&pdev->dev,
|
|
pbl->pg_size,
|
|
&pbl->pg_map_arr[i],
|
|
GFP_KERNEL);
|
|
if (!pbl->pg_arr[i])
|
|
goto fail;
|
|
pbl->pg_count++;
|
|
}
|
|
|
|
return 0;
|
|
fail:
|
|
bng_free_pbl(res, pbl);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
void bng_re_free_hwq(struct bng_re_res *res,
|
|
struct bng_re_hwq *hwq)
|
|
{
|
|
int i;
|
|
|
|
if (!hwq->max_elements)
|
|
return;
|
|
if (hwq->level >= BNG_PBL_LVL_MAX)
|
|
return;
|
|
|
|
for (i = 0; i < hwq->level + 1; i++)
|
|
bng_free_pbl(res, &hwq->pbl[i]);
|
|
|
|
hwq->level = BNG_PBL_LVL_MAX;
|
|
hwq->max_elements = 0;
|
|
hwq->element_size = 0;
|
|
hwq->prod = 0;
|
|
hwq->cons = 0;
|
|
}
|
|
|
|
/* All HWQs are power of 2 in size */
|
|
int bng_re_alloc_init_hwq(struct bng_re_hwq *hwq,
|
|
struct bng_re_hwq_attr *hwq_attr)
|
|
{
|
|
u32 npages, pg_size;
|
|
struct bng_re_sg_info sginfo = {};
|
|
u32 depth, stride, npbl, npde;
|
|
dma_addr_t *src_phys_ptr, **dst_virt_ptr;
|
|
struct bng_re_res *res;
|
|
struct pci_dev *pdev;
|
|
int i, rc, lvl;
|
|
|
|
res = hwq_attr->res;
|
|
pdev = res->pdev;
|
|
pg_size = hwq_attr->sginfo->pgsize;
|
|
hwq->level = BNG_PBL_LVL_MAX;
|
|
|
|
depth = roundup_pow_of_two(hwq_attr->depth);
|
|
stride = roundup_pow_of_two(hwq_attr->stride);
|
|
|
|
npages = (depth * stride) / pg_size;
|
|
if ((depth * stride) % pg_size)
|
|
npages++;
|
|
if (!npages)
|
|
return -EINVAL;
|
|
hwq_attr->sginfo->npages = npages;
|
|
|
|
if (npages == MAX_PBL_LVL_0_PGS && !hwq_attr->sginfo->nopte) {
|
|
/* This request is Level 0, map PTE */
|
|
rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_0], hwq_attr->sginfo);
|
|
if (rc)
|
|
goto fail;
|
|
hwq->level = BNG_PBL_LVL_0;
|
|
goto done;
|
|
}
|
|
|
|
if (npages >= MAX_PBL_LVL_0_PGS) {
|
|
if (npages > MAX_PBL_LVL_1_PGS) {
|
|
u32 flag = PTU_PTE_VALID;
|
|
/* 2 levels of indirection */
|
|
npbl = npages >> MAX_PBL_LVL_1_PGS_SHIFT;
|
|
if (npages % BIT(MAX_PBL_LVL_1_PGS_SHIFT))
|
|
npbl++;
|
|
npde = npbl >> MAX_PDL_LVL_SHIFT;
|
|
if (npbl % BIT(MAX_PDL_LVL_SHIFT))
|
|
npde++;
|
|
/* Alloc PDE pages */
|
|
sginfo.pgsize = npde * pg_size;
|
|
sginfo.npages = 1;
|
|
rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_0], &sginfo);
|
|
if (rc)
|
|
goto fail;
|
|
|
|
/* Alloc PBL pages */
|
|
sginfo.npages = npbl;
|
|
sginfo.pgsize = PAGE_SIZE;
|
|
rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_1], &sginfo);
|
|
if (rc)
|
|
goto fail;
|
|
/* Fill PDL with PBL page pointers */
|
|
dst_virt_ptr =
|
|
(dma_addr_t **)hwq->pbl[BNG_PBL_LVL_0].pg_arr;
|
|
src_phys_ptr = hwq->pbl[BNG_PBL_LVL_1].pg_map_arr;
|
|
for (i = 0; i < hwq->pbl[BNG_PBL_LVL_1].pg_count; i++)
|
|
dst_virt_ptr[0][i] = src_phys_ptr[i] | flag;
|
|
|
|
/* Alloc or init PTEs */
|
|
rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_2],
|
|
hwq_attr->sginfo);
|
|
if (rc)
|
|
goto fail;
|
|
hwq->level = BNG_PBL_LVL_2;
|
|
if (hwq_attr->sginfo->nopte)
|
|
goto done;
|
|
/* Fill PBLs with PTE pointers */
|
|
dst_virt_ptr =
|
|
(dma_addr_t **)hwq->pbl[BNG_PBL_LVL_1].pg_arr;
|
|
src_phys_ptr = hwq->pbl[BNG_PBL_LVL_2].pg_map_arr;
|
|
for (i = 0; i < hwq->pbl[BNG_PBL_LVL_2].pg_count; i++) {
|
|
dst_virt_ptr[PTR_PG(i)][PTR_IDX(i)] =
|
|
src_phys_ptr[i] | PTU_PTE_VALID;
|
|
}
|
|
if (hwq_attr->type == BNG_HWQ_TYPE_QUEUE) {
|
|
/* Find the last pg of the size */
|
|
i = hwq->pbl[BNG_PBL_LVL_2].pg_count;
|
|
dst_virt_ptr[PTR_PG(i - 1)][PTR_IDX(i - 1)] |=
|
|
PTU_PTE_LAST;
|
|
if (i > 1)
|
|
dst_virt_ptr[PTR_PG(i - 2)]
|
|
[PTR_IDX(i - 2)] |=
|
|
PTU_PTE_NEXT_TO_LAST;
|
|
}
|
|
} else { /* pages < 512 npbl = 1, npde = 0 */
|
|
u32 flag = PTU_PTE_VALID;
|
|
|
|
/* 1 level of indirection */
|
|
npbl = npages >> MAX_PBL_LVL_1_PGS_SHIFT;
|
|
if (npages % BIT(MAX_PBL_LVL_1_PGS_SHIFT))
|
|
npbl++;
|
|
sginfo.npages = npbl;
|
|
sginfo.pgsize = PAGE_SIZE;
|
|
/* Alloc PBL page */
|
|
rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_0], &sginfo);
|
|
if (rc)
|
|
goto fail;
|
|
/* Alloc or init PTEs */
|
|
rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_1],
|
|
hwq_attr->sginfo);
|
|
if (rc)
|
|
goto fail;
|
|
hwq->level = BNG_PBL_LVL_1;
|
|
if (hwq_attr->sginfo->nopte)
|
|
goto done;
|
|
/* Fill PBL with PTE pointers */
|
|
dst_virt_ptr =
|
|
(dma_addr_t **)hwq->pbl[BNG_PBL_LVL_0].pg_arr;
|
|
src_phys_ptr = hwq->pbl[BNG_PBL_LVL_1].pg_map_arr;
|
|
for (i = 0; i < hwq->pbl[BNG_PBL_LVL_1].pg_count; i++)
|
|
dst_virt_ptr[PTR_PG(i)][PTR_IDX(i)] =
|
|
src_phys_ptr[i] | flag;
|
|
if (hwq_attr->type == BNG_HWQ_TYPE_QUEUE) {
|
|
/* Find the last pg of the size */
|
|
i = hwq->pbl[BNG_PBL_LVL_1].pg_count;
|
|
dst_virt_ptr[PTR_PG(i - 1)][PTR_IDX(i - 1)] |=
|
|
PTU_PTE_LAST;
|
|
if (i > 1)
|
|
dst_virt_ptr[PTR_PG(i - 2)]
|
|
[PTR_IDX(i - 2)] |=
|
|
PTU_PTE_NEXT_TO_LAST;
|
|
}
|
|
}
|
|
}
|
|
done:
|
|
hwq->prod = 0;
|
|
hwq->cons = 0;
|
|
hwq->pdev = pdev;
|
|
hwq->depth = hwq_attr->depth;
|
|
hwq->max_elements = hwq->depth;
|
|
hwq->element_size = stride;
|
|
hwq->qe_ppg = pg_size / stride;
|
|
/* For direct access to the elements */
|
|
lvl = hwq->level;
|
|
if (hwq_attr->sginfo->nopte && hwq->level)
|
|
lvl = hwq->level - 1;
|
|
hwq->pbl_ptr = hwq->pbl[lvl].pg_arr;
|
|
hwq->pbl_dma_ptr = hwq->pbl[lvl].pg_map_arr;
|
|
spin_lock_init(&hwq->lock);
|
|
|
|
return 0;
|
|
fail:
|
|
bng_re_free_hwq(res, hwq);
|
|
return -ENOMEM;
|
|
}
|