[PATCH 10/15] yunsilicon: add RDMA PD/MR
flat view
COOLING9d
From: Xin Tian <hidden>
Date: 2026-10-12 11:44:24
Also in:
linux-rdma, lkml
Subsystem:
infiniband subsystem, networking drivers, the rest · Maintainers:
Jason Gunthorpe, Leon Romanovsky, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Add protection domains and memory regions: the PD allocation verbs, the kernel and user MR registration paths (including dmabuf), and the DMA address translation helpers they build on. Signed-off-by: Xin Tian <redacted> --- drivers/infiniband/hw/xsc/Makefile | 2 +- drivers/infiniband/hw/xsc/main.c | 44 +++ drivers/infiniband/hw/xsc/mem.c | 161 +++++++++ drivers/infiniband/hw/xsc/mr.c | 313 ++++++++++++++++++ .../net/ethernet/yunsilicon/xsc/pci/Makefile | 2 +- drivers/net/ethernet/yunsilicon/xsc/pci/mr.c | 212 ++++++++++++ drivers/net/ethernet/yunsilicon/xsc/pci/mr.h | 18 + drivers/net/ethernet/yunsilicon/xsc/pci/pd.c | 55 +++ drivers/net/ethernet/yunsilicon/xsc/pci/pd.h | 14 + 9 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 drivers/infiniband/hw/xsc/mem.c create mode 100644 drivers/infiniband/hw/xsc/mr.c create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/mr.c create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/mr.h create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/pd.c create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/pd.h
diff --git a/drivers/infiniband/hw/xsc/Makefile b/drivers/infiniband/hw/xsc/Makefile
index 6c4ec1ca0..b535a6653 100644
--- a/drivers/infiniband/hw/xsc/Makefile
+++ b/drivers/infiniband/hw/xsc/Makefile@@ -6,4 +6,4 @@ ccflags-y := -I$(srctree)/drivers/net/ethernet/yunsilicon/xsc obj-$(CONFIG_INFINIBAND_XSC) += xsc_ib.o -xsc_ib-y := main.o +xsc_ib-y := main.o mem.o mr.o
diff --git a/drivers/infiniband/hw/xsc/main.c b/drivers/infiniband/hw/xsc/main.c
index 9d53a89be..bd2c23b51 100644
--- a/drivers/infiniband/hw/xsc/main.c
+++ b/drivers/infiniband/hw/xsc/main.c@@ -382,6 +382,42 @@ static int xsc_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma reg_size, vma->vm_page_prot); } +int xsc_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) +{ + struct ib_device *ibdev = ibpd->device; + struct xsc_ib_alloc_pd_resp resp; + struct xsc_ib_pd *pd; + int err; + + pd = to_xpd(ibpd); + + err = xsc_core_alloc_pd(to_xdev(ibdev)->xdev, &pd->pdn); + if (err) + return err; + + if (udata) { + resp.pdn = pd->pdn; + if (ib_copy_to_udata(udata, &resp, sizeof(resp))) { + xsc_core_dealloc_pd(to_xdev(ibdev)->xdev, pd->pdn); + return -EFAULT; + } + } else { + pd->pa_lkey = 0; + } + + return 0; +} + +int xsc_ib_dealloc_pd(struct ib_pd *pd, struct ib_udata *udata) +{ + struct xsc_ib_dev *dev = to_xdev(pd->device); + struct xsc_ib_pd *xpd = to_xpd(pd); + + xsc_core_dealloc_pd(dev->xdev, xpd->pdn); + + return 0; +} + static int xsc_port_immutable(struct ib_device *ibdev, u32 port_num, struct ib_port_immutable *immutable) {
@@ -525,16 +561,23 @@ static void xsc_ib_dev_setting(struct xsc_ib_dev *dev) dev->ib_dev.ops.dealloc_ucontext = xsc_ib_dealloc_ucontext; dev->ib_dev.ops.mmap = xsc_ib_mmap; + dev->ib_dev.ops.alloc_pd = xsc_ib_alloc_pd; + dev->ib_dev.ops.dealloc_pd = xsc_ib_dealloc_pd; dev->ib_dev.ops.get_link_layer = xsc_ib_port_link_layer; dev->ib_dev.ops.get_netdev = xsc_get_netdev; + dev->ib_dev.ops.get_dma_mr = xsc_ib_get_dma_mr; + dev->ib_dev.ops.reg_user_mr = xsc_ib_reg_user_mr; /* optional */ + dev->ib_dev.ops.reg_user_mr_dmabuf = xsc_ib_reg_user_mr_dmabuf; + dev->ib_dev.ops.dereg_mr = xsc_ib_dereg_mr; dev->ib_dev.ops.get_port_immutable = xsc_port_immutable; dev->ib_dev.ops.get_dev_fw_str = xsc_ib_get_dev_fw_str; dev->ib_dev.ops INIT_RDMA_OBJ_SIZE(ib_ucontext, xsc_ib_ucontext, ibucontext); + dev->ib_dev.ops INIT_RDMA_OBJ_SIZE(ib_pd, xsc_ib_pd, ibpd); } static void xsc_get_port_state(struct net_device *ndev, enum xsc_dev_event *ev)
@@ -647,6 +690,7 @@ static int init_one(struct xsc_core_device *xdev, + xsc_reg_local_dma_mr(xdev); err = ib_register_device(&dev->ib_dev, dev->ib_dev.name, dev->xdev->device);
diff --git a/drivers/infiniband/hw/xsc/mem.c b/drivers/infiniband/hw/xsc/mem.c
new file mode 100644
index 000000000..2fbce9943
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/mem.c@@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd. + * All rights reserved. + */ + +#include <linux/module.h> +#include <rdma/ib_umem.h> +#include "xsc_ib.h" + +/** + * __xsc_ib_cont_pages - scan a umem for compound pages + * @umem: umem object to scan + * @addr: ib virtual address requested by the user + * @max_page_shift: upper limit for the detected page shift, 0 for none + * @count: number of PAGE_SIZE pages covered by umem + * @shift: page shift for the compound pages found in the region + * @ncont: number of compound pages + */ +static void __xsc_ib_cont_pages(struct ib_umem *umem, u64 addr, + unsigned long max_page_shift, + int *count, int *shift, int *ncont) +{ + unsigned long tmp; + unsigned long m; + u64 base = ~0, p = 0; + u64 len, pfn; + int i = 0; + struct scatterlist *sg; + int entry; + /* TODO: need peer mem support */ + unsigned long page_shift = PAGE_SHIFT; + + addr = addr >> page_shift; + tmp = (unsigned long)addr; + m = find_first_bit(&tmp, BITS_PER_LONG); + if (max_page_shift) + m = min_t(unsigned long, max_page_shift - page_shift, m); + for_each_sgtable_dma_sg(&umem->sgt_append.sgt, sg, entry) { + len = sg_dma_len(sg) >> page_shift; + pfn = sg_dma_address(sg) >> page_shift; + if (base + p != pfn) { + /* If either the offset or the new + * base are unaligned update m + */ + tmp = (unsigned long)(pfn | p); + if (!IS_ALIGNED(tmp, 1 << m)) + m = find_first_bit(&tmp, BITS_PER_LONG); + + base = pfn; + p = 0; + } + + p += len; + i += len; + } + + if (i) { + m = min_t(unsigned long, ilog2(roundup_pow_of_two(i)), m); + *ncont = DIV_ROUND_UP(i, (1 << m)); + } else { + m = 0; + *ncont = 0; + } + *shift = page_shift + m; + *count = i; +} + +void xsc_ib_cont_pages(struct ib_umem *umem, u64 addr, + int *count, int *shift, int *ncont) +{ + /* no limit for page_shift */ + __xsc_ib_cont_pages(umem, addr, 0, count, shift, ncont); +} + +static void __xsc_ib_populate_pas(struct xsc_ib_dev *dev, struct ib_umem *umem, + int page_shift, size_t offset, size_t num_pages, + __be64 *pas, int access_flags, bool need_to_devide) +{ + unsigned long umem_page_shift = PAGE_SHIFT; + int shift = page_shift - umem_page_shift; + int mask = (1 << shift) - 1; + int i = 0; + int k, idx; + u64 cur = 0; + u64 base; + int len; + struct scatterlist *sg; + int entry; + + for_each_sgtable_dma_sg(&umem->sgt_append.sgt, sg, entry) { + len = sg_dma_len(sg) >> umem_page_shift; + if (need_to_devide) + len = sg_dma_len(sg) >> PAGE_SHIFT_4K; + else + len = sg_dma_len(sg) >> umem_page_shift; + base = sg_dma_address(sg); + + /* Skip elements below offset */ + if (i + len < offset << shift) { + i += len; + continue; + } + + /* Skip pages below offset */ + if (i < offset << shift) { + k = (offset << shift) - i; + i = offset << shift; + } else { + k = 0; + } + + for (; k < len; k++) { + if (!(i & mask)) { + if (need_to_devide) + cur = base + (k << PAGE_SHIFT_4K); + else + cur = base + (k << umem_page_shift); + cur |= access_flags; + idx = (i >> shift) - offset; + + pas[idx] = cpu_to_be64(cur); + ibdev_dbg(&dev->ib_dev, "pas[%d] 0x%llx\n", + i >> shift, be64_to_cpu(pas[idx])); + } + i++; + + /* Stop after num_pages reached */ + if (i >> shift >= offset + num_pages) + return; + } + } +} + +void xsc_ib_populate_pas(struct xsc_ib_dev *dev, struct ib_umem *umem, + int page_shift, __be64 *pas, int npages, bool need_to_devide) +{ + return __xsc_ib_populate_pas(dev, umem, page_shift, 0, + npages, pas, 0, need_to_devide); +} + +int xsc_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset) +{ + u64 page_size; + u64 page_mask; + u64 off_size; + u64 off_mask; + u64 buf_off; + + page_size = 1 << page_shift; + page_mask = page_size - 1; + buf_off = addr & page_mask; + off_size = page_size >> 6; + off_mask = off_size - 1; + + if (buf_off & off_mask) + return -EINVAL; + + *offset = buf_off >> ilog2(off_size); + return 0; +}
diff --git a/drivers/infiniband/hw/xsc/mr.c b/drivers/infiniband/hw/xsc/mr.c
new file mode 100644
index 000000000..efd685e57
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/mr.c@@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd. + * All rights reserved. + */ + +#include <linux/kref.h> +#include <linux/random.h> +#include <linux/debugfs.h> +#include <linux/export.h> +#include <linux/dma-buf.h> +#include <linux/dma-resv.h> +#include <rdma/ib_umem.h> +#include <rdma/iter.h> +#include "common/xsc_cmd.h" +#include <linux/dma-direct.h> +#include "xsc_ib.h" + +enum { + DEF_CACHE_SIZE = 10, +}; + +struct ib_mr *xsc_ib_get_dma_mr(struct ib_pd *pd, int acc) +{ + struct xsc_ib_dev *dev = to_xdev(pd->device); + struct xsc_core_device *xdev = dev->xdev; + struct xsc_register_mr_mbox_in *in; + struct xsc_register_mr_request *req; + struct xsc_ib_mr *mr; + int err; + + mr = kzalloc_obj(*mr, GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + in = kzalloc_obj(*in, GFP_KERNEL); + if (!in) { + err = -ENOMEM; + goto err_free; + } + + req = &in->req; + req->acc = convert_access(acc); + req->va_base = 0; + req->map_en = !(XSC_MPT_MAP_EN); + + err = xsc_core_create_mkey(xdev, &mr->xmr); + if (err) + goto err_in; + req->mkey = cpu_to_be32(mr->xmr.key); + err = xsc_core_register_mr(xdev, &mr->xmr, in, sizeof(*in)); + if (err) + goto err_reg_mr; + kfree(in); + mr->ibmr.lkey = mr->xmr.key; + mr->ibmr.rkey = mr->xmr.key; + mr->umem = NULL; + + return &mr->ibmr; +err_reg_mr: + xsc_core_destroy_mkey(xdev, &mr->xmr); +err_in: + kfree(in); + +err_free: + kfree(mr); + + return ERR_PTR(err); +} + +static struct xsc_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr, + u64 length, struct ib_umem *umem, + int npages, int page_shift, + int access_flags) +{ + struct xsc_ib_dev *dev = to_xdev(pd->device); + struct xsc_register_mr_mbox_in *in; + struct xsc_ib_mr *mr; + struct ib_block_iter biter; + __be64 *pas; + int inlen; + int err; + + mr = kzalloc_obj(*mr, GFP_KERNEL); + if (!mr) { + err = -ENOMEM; + goto err_0; + } + + inlen = sizeof(*in) + sizeof(*in->req.pas) * npages; + in = xsc_vzalloc(inlen); + if (!in) { + err = -ENOMEM; + goto err_1; + } + err = xsc_core_create_mkey(dev->xdev, &mr->xmr); + if (err) { + ibdev_warn(&dev->ib_dev, "create mkey failed\n"); + goto err_2; + } + + pas = in->req.pas; + rdma_umem_for_each_dma_block(umem, &biter, 1UL << page_shift) { + *pas = cpu_to_be64(rdma_block_iter_dma_address(&biter)); + pas++; + } + + in->req.acc = convert_access(access_flags); + in->req.pa_num = cpu_to_be32(npages); + in->req.pdn = cpu_to_be32(to_xpd(pd)->pdn); + in->req.va_base = cpu_to_be64(virt_addr); + in->req.map_en = XSC_MPT_MAP_EN; + in->req.len = cpu_to_be64(length); + in->req.page_mode = xsc_get_mr_page_mode(dev->xdev, page_shift); + ibdev_dbg(&dev->ib_dev, "read_flush hwconfig %s\n", + dev->xdev->read_flush ? "enable" : "disable"); + in->req.is_gpu = 0; + in->req.mkey = cpu_to_be32(mr->xmr.key); + err = xsc_core_register_mr(dev->xdev, &mr->xmr, in, inlen); + if (err) { + ibdev_warn(&dev->ib_dev, "register mr failed, err = %d\n", err); + goto err_reg_mr; + } + mr->umem = umem; + xsc_vfree(in); + + ibdev_dbg(&dev->ib_dev, "mkey = 0x%x\n", mr->xmr.key); + + return mr; +err_reg_mr: + xsc_core_destroy_mkey(dev->xdev, &mr->xmr); +err_2: + xsc_vfree(in); +err_1: + kfree(mr); +err_0: + return ERR_PTR(err); +} + +struct ib_mr *xsc_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt_addr, int access_flags, + struct ib_dmah *dmah, + struct ib_udata *udata) +{ + struct xsc_ib_dev *dev = to_xdev(pd->device); + struct xsc_ib_mr *mr = NULL; + struct ib_umem *umem; + unsigned long pgsz; + int page_shift; + int npages; + int err; + + if (dmah) + return ERR_PTR(-EOPNOTSUPP); + + if (length > dev->xdev->caps.max_mr_size) { + ibdev_err(&dev->ib_dev, "reg user mr length(%llu) exceeded.\n", length); + return ERR_PTR(-EINVAL); + } + + ibdev_dbg(&dev->ib_dev, "start 0x%llx, virt_addr 0x%llx, length 0x%llx\n", + start, virt_addr, length); + + umem = ib_umem_get_va(&dev->ib_dev, start, length, access_flags); + if (IS_ERR(umem)) { + ibdev_dbg(&dev->ib_dev, "umem get failed\n"); + return (void *)umem; + } + + pgsz = ib_umem_find_best_pgsz(umem, XSC_MR_PAGE_CAP_MASK, virt_addr); + if (!pgsz) { + ibdev_warn(&dev->ib_dev, "find best page size failed\n"); + err = -EINVAL; + goto error; + } + + page_shift = order_base_2(pgsz); + npages = ib_umem_num_dma_blocks(umem, pgsz); + + ibdev_dbg(&dev->ib_dev, "npages %d, page_shift %d\n", npages, page_shift); + + mr = reg_create(pd, virt_addr, length, umem, npages, page_shift, + access_flags); + if (IS_ERR(mr)) { + err = PTR_ERR(mr); + goto error; + } + + ibdev_dbg(&dev->ib_dev, "mkey 0x%x\n", mr->xmr.key); + + mr->umem = umem; + mr->npages = npages; + mr->ibmr.lkey = mr->xmr.key; + mr->ibmr.rkey = mr->xmr.key; + mr->ibmr.length = length; + return &mr->ibmr; + +error: + ib_umem_release(umem); + return ERR_PTR(err); +} + +struct ib_mr *xsc_ib_reg_user_mr_dmabuf(struct ib_pd *pd, u64 offset, + u64 length, u64 virt_addr, + int fd, int access_flags, + struct ib_dmah *dmah, + struct uverbs_attr_bundle *attrs) +{ + struct xsc_ib_dev *dev = to_xdev(pd->device); + struct ib_umem_dmabuf *umem_dmabuf; + struct ib_umem *umem; + struct xsc_ib_mr *mr; + unsigned long pgsz; + int page_shift; + int npages; + int err; + + if (!IS_ENABLED(CONFIG_INFINIBAND_USER_MEM)) + return ERR_PTR(-EOPNOTSUPP); + + if (dmah) + return ERR_PTR(-EOPNOTSUPP); + + umem_dmabuf = ib_umem_dmabuf_get_pinned(&dev->ib_dev, + offset, length, + fd, access_flags); + if (IS_ERR(umem_dmabuf)) { + ibdev_dbg(&dev->ib_dev, "umem_dmabuf get failed (%ld)\n", + PTR_ERR(umem_dmabuf)); + return ERR_CAST(umem_dmabuf); + } + umem = &umem_dmabuf->umem; + + pgsz = ib_umem_find_best_pgsz(umem, XSC_MR_PAGE_CAP_MASK, virt_addr); + if (!pgsz) { + ibdev_warn(&dev->ib_dev, "find best page size failed\n"); + err = -EINVAL; + goto err_release; + } + + page_shift = order_base_2(pgsz); + npages = ib_umem_num_dma_blocks(umem, pgsz); + + mr = reg_create(pd, virt_addr, length, umem, npages, page_shift, + access_flags); + if (IS_ERR(mr)) { + err = PTR_ERR(mr); + goto err_release; + } + + mr->umem = umem; + mr->npages = npages; + mr->ibmr.lkey = mr->xmr.key; + mr->ibmr.rkey = mr->xmr.key; + mr->ibmr.length = length; + + return &mr->ibmr; +err_release: + ib_umem_release(umem); + return ERR_PTR(err); +} + +int xsc_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) +{ + struct xsc_ib_dev *dev = to_xdev(ibmr->device); + struct xsc_ib_mr *mr = to_xmr(ibmr); + struct ib_umem *umem = mr->umem; + int err; + + ibdev_dbg(&dev->ib_dev, "dereg mkey = 0x%x\n", mr->xmr.key); + + if (mr->npages) { + err = xsc_core_dereg_mr(dev->xdev, &mr->xmr); + if (err) { + ibdev_warn(&dev->ib_dev, "failed to dereg mr 0x%x (%d)\n", + mr->xmr.key, err); + return err; + } + } + err = xsc_core_destroy_mkey(dev->xdev, &mr->xmr); + if (err) { + ibdev_warn(&dev->ib_dev, "failed to destroy mkey 0x%x (%d)\n", + mr->xmr.key, err); + return err; + } + + if (umem) + ib_umem_release(umem); + + kfree(mr); + + return 0; +} + +void xsc_reg_local_dma_mr(struct xsc_core_device *dev) +{ + struct xsc_register_mr_mbox_in in; + int err = 0; + + in.req.pdn = 0; + in.req.pa_num = 0; + in.req.len = 0; + in.req.mkey = cpu_to_be32(0xFF); + in.req.acc = XSC_PERM_LOCAL_WRITE | XSC_PERM_LOCAL_READ; + in.req.page_mode = xsc_get_mr_page_mode(dev, PAGE_SHIFT_4K); + in.req.is_gpu = 0; + in.req.map_en = !(XSC_MPT_MAP_EN); + in.req.va_base = 0; + + err = xsc_core_register_mr(dev, NULL, &in, sizeof(in)); + if (err) + dev_err(&dev->pdev->dev, "\n"); +}
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile b/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile
index 87481db3f..065d52ebf 100644
--- a/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile@@ -6,4 +6,4 @@ ccflags-y += -I$(srctree)/drivers/net/ethernet/yunsilicon/xsc obj-$(CONFIG_YUNSILICON_XSC_PCI) += xsc_pci.o -xsc_pci-y := main.o cmdq.o hw.o qp.o cq.o link.o alloc.o eq.o pci_irq.o adev.o vport.o hal/andes_impl.o +xsc_pci-y := main.o cmdq.o hw.o qp.o cq.o pd.o mr.o link.o alloc.o eq.o pci_irq.o adev.o vport.o hal/andes_impl.o
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/mr.c b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.c
new file mode 100644
index 000000000..5c68f0fd8
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.c@@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd. + * All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "common/xsc_core.h" +#include "common/xsc_cmd_api.h" +#include "hal/xsc_hal.h" +#include "mr.h" + +int xsc_core_create_mkey(struct xsc_core_device *xdev, struct xsc_core_mr *mr) +{ + struct xsc_create_mkey_mbox_in in; + struct xsc_create_mkey_mbox_out out; + u8 key; + int err; + + memset(&in, 0, sizeof(in)); + memset(&out, 0, sizeof(out)); + + spin_lock(&xdev->dev_res->mkey_lock); + key = 0x80 + xdev->dev_res->mkey_key++; + spin_unlock(&xdev->dev_res->mkey_lock); + + in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_CREATE_MKEY); + err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out)); + if (err) { + pci_err(xdev->pdev, "failed to create mkey, err = %d\n", err); + return err; + } + + if (out.hdr.status) { + pci_err(xdev->pdev, "failed to create mkey, status = %d\n", out.hdr.status); + return xsc_cmd_status_to_err(&out.hdr); + } + + mr->key = xsc_idx_to_mkey(xdev, be32_to_cpu(out.mkey) & 0xffffff) | key; + pci_dbg(xdev->pdev, "out 0x%x, key 0x%x, mkey 0x%x\n", + be32_to_cpu(out.mkey), key, mr->key); + + return 0; +} +EXPORT_SYMBOL_GPL(xsc_core_create_mkey); + +int xsc_core_destroy_mkey(struct xsc_core_device *xdev, struct xsc_core_mr *mr) +{ + struct xsc_destroy_mkey_mbox_in in; + struct xsc_destroy_mkey_mbox_out out; + int err; + + memset(&in, 0, sizeof(in)); + memset(&out, 0, sizeof(out)); + + in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DESTROY_MKEY); + in.mkey = cpu_to_be32(mr->key); + + err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out)); + if (err) + return err; + + if (out.hdr.status) + return xsc_cmd_status_to_err(&out.hdr); + + return 0; +} +EXPORT_SYMBOL_GPL(xsc_core_destroy_mkey); + +static int xsc_set_mpt_via_cmdq(struct xsc_core_device *xdev, + struct xsc_register_mr_mbox_in *in_cmd, + u32 *mtt_base) +{ + struct xsc_register_mr_request *req = &in_cmd->req; + struct xsc_set_mpt_mbox_in *in; + struct xsc_set_mpt_mbox_out out; + u64 mem_size; + int err; + + in = kzalloc_obj(*in, GFP_KERNEL); + if (!in) + return -ENOMEM; + + mem_size = be64_to_cpu(req->len); + in->mpt_item.pdn = req->pdn; + in->mpt_item.pa_num = req->pa_num; + in->mpt_item.len = cpu_to_be32((u32)mem_size); + in->mpt_item.len_h = cpu_to_be32((u32)(mem_size >> 32)); + in->mpt_item.mkey = req->mkey; + in->mpt_item.is_gpu = req->is_gpu; + in->mpt_item.acc = req->acc; + in->mpt_item.page_mode = req->page_mode; + in->mpt_item.map_en = req->map_en; + in->mpt_item.va_base = req->va_base; + in->hdr.opcode = cpu_to_be16(XSC_CMD_OP_SET_MPT); + + memset(&out, 0, sizeof(out)); + err = xsc_cmd_exec(xdev, in, sizeof(*in), &out, sizeof(out)); + kfree(in); + if (err || out.hdr.status) { + pci_err(xdev->pdev, "failed to set mpt, err = %d, status = %d\n", + err, out.hdr.status); + return -EINVAL; + } + + *mtt_base = be32_to_cpu(out.mtt_base); + return 0; +} + +#define PA_NUM_PER_CMD 1024 + +static int xsc_set_mtt_via_cmdq(struct xsc_core_device *xdev, + struct xsc_register_mr_mbox_in *in_cmd, + u32 mtt_base) +{ + struct xsc_register_mr_request *req = &in_cmd->req; + struct xsc_set_mtt_mbox_in *seg_in; + struct xsc_set_mtt_mbox_out seg_out; + int tot_pg_num = be32_to_cpu(req->pa_num); + int tot_seg_num, seg_pa_num, seg_idx; + int pa_idx_base = 0; + int in_len, err, i; + + tot_seg_num = (tot_pg_num & (PA_NUM_PER_CMD - 1)) ? + ((tot_pg_num >> 10) + 1) : (tot_pg_num >> 10); + + for (seg_idx = 0; seg_idx < tot_seg_num; seg_idx++) { + seg_pa_num = (seg_idx != tot_seg_num - 1) ? PA_NUM_PER_CMD : + (tot_pg_num - ((tot_seg_num - 1) << 10)); + in_len = (seg_pa_num << 3) + sizeof(*seg_in); + seg_in = kzalloc(in_len, GFP_KERNEL); + if (!seg_in) + return -ENOMEM; + + seg_in->mtt_setting.mtt_base = cpu_to_be32(mtt_base); + seg_in->mtt_setting.pa_num = cpu_to_be32(seg_pa_num); + for (i = 0; i < seg_pa_num; i++) + seg_in->mtt_setting.pas[i] = req->pas[pa_idx_base + i]; + seg_in->hdr.opcode = cpu_to_be16(XSC_CMD_OP_SET_MTT); + + memset(&seg_out, 0, sizeof(seg_out)); + pci_dbg(xdev->pdev, + "set mtt seg %d, pa_num %d, pa_idx_base %d, tot_seg %d\n", + seg_idx, seg_pa_num, pa_idx_base, tot_seg_num); + err = xsc_cmd_exec(xdev, seg_in, in_len, &seg_out, sizeof(seg_out)); + kfree(seg_in); + if (err || seg_out.hdr.status) { + pci_err(xdev->pdev, "failed to set mtt seg %d, err = %d, status = %d\n", + seg_idx, err, seg_out.hdr.status); + return -EINVAL; + } + + pa_idx_base += seg_pa_num; + mtt_base += seg_pa_num; + } + + return 0; +} + +static int xsc_dereg_mr_via_cmdq(struct xsc_core_device *xdev, u32 mkey) +{ + struct xsc_unregister_mr_mbox_in in; + struct xsc_unregister_mr_mbox_out out; + int err; + + memset(&in, 0, sizeof(in)); + memset(&out, 0, sizeof(out)); + in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DEREG_MR); + in.mkey = cpu_to_be32(xsc_mkey_to_idx(xdev, mkey)); + + err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out)); + if (err || out.hdr.status) { + pci_err(xdev->pdev, "failed to dereg mr, err = %d, status = %d\n", + err, out.hdr.status); + return -EINVAL; + } + + return 0; +} + +int xsc_core_register_mr(struct xsc_core_device *xdev, struct xsc_core_mr *mr, + struct xsc_register_mr_mbox_in *in, int inlen) +{ + u32 mtt_base; + int err; + + in->hdr.opcode = cpu_to_be16(XSC_CMD_OP_REG_MR); + + err = xsc_set_mpt_via_cmdq(xdev, in, &mtt_base); + if (err) { + pci_err(xdev->pdev, "failed to set mpt via cmdq\n"); + return err; + } + + err = xsc_set_mtt_via_cmdq(xdev, in, mtt_base); + if (err) { + pci_err(xdev->pdev, "failed to set mtt via cmdq\n"); + xsc_dereg_mr_via_cmdq(xdev, be32_to_cpu(in->req.mkey)); + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(xsc_core_register_mr); + +int xsc_core_dereg_mr(struct xsc_core_device *xdev, struct xsc_core_mr *mr) +{ + return xsc_dereg_mr_via_cmdq(xdev, mr->key); +} +EXPORT_SYMBOL_GPL(xsc_core_dereg_mr);
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/mr.h b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.h
new file mode 100644
index 000000000..edafecda6
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.h@@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd. + * All rights reserved. + */ + +#ifndef __XSC_MR_H +#define __XSC_MR_H + +#include "common/xsc_core.h" +#include "common/xsc_cmd.h" + +int xsc_core_create_mkey(struct xsc_core_device *xdev, struct xsc_core_mr *mr); +int xsc_core_destroy_mkey(struct xsc_core_device *xdev, struct xsc_core_mr *mr); +int xsc_core_register_mr(struct xsc_core_device *xdev, struct xsc_core_mr *mr, + struct xsc_register_mr_mbox_in *in, int inlen); +int xsc_core_dereg_mr(struct xsc_core_device *xdev, struct xsc_core_mr *mr); + +#endif /* __XSC_MR_H */
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/pd.c b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.c
new file mode 100644
index 000000000..7969dfefc
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.c@@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd. + * All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "common/xsc_core.h" +#include "common/xsc_cmd_api.h" +#include "pd.h" + +int xsc_core_alloc_pd(struct xsc_core_device *xdev, u32 *pdn) +{ + struct xsc_alloc_pd_mbox_in in; + struct xsc_alloc_pd_mbox_out out; + int err; + + memset(&in, 0, sizeof(in)); + memset(&out, 0, sizeof(out)); + in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_ALLOC_PD); + + err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out)); + if (err) + return err; + + if (out.hdr.status) + return xsc_cmd_status_to_err(&out.hdr); + + *pdn = be32_to_cpu(out.pdn) & 0xffffff; + return 0; +} +EXPORT_SYMBOL_GPL(xsc_core_alloc_pd); + +int xsc_core_dealloc_pd(struct xsc_core_device *xdev, u32 pdn) +{ + struct xsc_dealloc_pd_mbox_in in; + struct xsc_dealloc_pd_mbox_out out; + int err; + + memset(&in, 0, sizeof(in)); + memset(&out, 0, sizeof(out)); + in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DEALLOC_PD); + in.pdn = cpu_to_be32(pdn); + + err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out)); + if (err) + return err; + + if (out.hdr.status) + return xsc_cmd_status_to_err(&out.hdr); + + return 0; +} +EXPORT_SYMBOL_GPL(xsc_core_dealloc_pd);
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/pd.h b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.h
new file mode 100644
index 000000000..d9a99baea
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.h@@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd. + * All rights reserved. + */ + +#ifndef __XSC_PD_H +#define __XSC_PD_H + +#include "common/xsc_core.h" + +int xsc_core_alloc_pd(struct xsc_core_device *xdev, u32 *pdn); +int xsc_core_dealloc_pd(struct xsc_core_device *xdev, u32 pdn); + +#endif /* __XSC_PD_H */