[PATCH v1 11/15] RDMA/xsc: add RDMA PD/MR

From
Xin Tian <hidden>
Date
2026-11-04 15:57:29
Also in
linux-rdma, lkml
Reply to
Xin Tian, 2026-11-04
Implement the protection domain and memory region verbs.

alloc_pd allocates a PD through firmware and returns its number to
userspace.  Before ib_register_device(), register the implicit local
DMA MR with mkey 0xFF covering the whole device: the MAD agent posts
QP1 traffic the moment registration returns and assumes lkey 0xFF is
valid, so it must exist by then.

User MRs are registered by building a firmware mailbox with the DMA
address list from the umem.  The page size is selected with
ib_umem_find_best_pgsz() among the 4K/64K/2M/1G sizes the device
supports, the page list is bounded by the firmware MTT capacity, and
the variable-length mailbox size is checked for overflow before
allocation.  dmabuf-backed MRs use ib_umem_dmabuf_get_pinned() and
share the same registration path.  get_dma_mr registers a whole-
device implicitly mapped MR for kernel callers.  Deregistration
releases the MPT/MTT and destroys the mkey.

On the core side, add the PD and MR firmware commands: mkey values
are allocated under a lock as a key byte combined with a hardware
index, and registration programs the MPT then writes the page
address list in chunks bounded by the maximum command payload,
rolling back the registration if a chunk fails.

Signed-off-by: Xin Tian <redacted>
---
 drivers/infiniband/hw/xsc/Makefile            |   2 +-
 drivers/infiniband/hw/xsc/main.c              |  45 +++
 drivers/infiniband/hw/xsc/mem.c               | 160 +++++++++
 drivers/infiniband/hw/xsc/mr.c                | 335 ++++++++++++++++++
 drivers/infiniband/hw/xsc/xsc_ib.h            |   2 +-
 .../ethernet/yunsilicon/xsc/common/driver.h   |   1 +
 .../net/ethernet/yunsilicon/xsc/pci/Makefile  |   2 +-
 drivers/net/ethernet/yunsilicon/xsc/pci/cq.c  |   8 +-
 drivers/net/ethernet/yunsilicon/xsc/pci/mr.c  | 202 +++++++++++
 drivers/net/ethernet/yunsilicon/xsc/pci/mr.h  |  18 +
 drivers/net/ethernet/yunsilicon/xsc/pci/pd.c  |  49 +++
 drivers/net/ethernet/yunsilicon/xsc/pci/pd.h  |  14 +
 12 files changed, 831 insertions(+), 7 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 322fad38b..a212a5888 100644
--- a/drivers/infiniband/hw/xsc/main.c
+++ b/drivers/infiniband/hw/xsc/main.c
@@ -391,6 +391,40 @@ static int xsc_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma
 				 NULL);
 }
 
+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);
+
+	return xsc_core_dealloc_pd(dev->xdev, xpd->pdn);
+}
+
 static int xsc_port_immutable(struct ib_device *ibdev, u32 port_num,
 			      struct ib_port_immutable *immutable)
 {
@@ -475,15 +509,22 @@ 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_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);
 }
 
 /*
@@ -616,6 +657,10 @@ static int init_one(struct xsc_core_device *xdev,
 	if (err)
 		goto err_free;
 
+	err = xsc_reg_local_dma_mr(xdev);
+	if (err)
+		goto err_free;
+
 	init_iommu_state(dev, xdev->device);
 	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..cb1376648
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/mem.c
@@ -0,0 +1,160 @@
+// 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, 1UL << 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) {
+		if (need_to_devide)
+			len = sg_dma_len(sg) >> XSC_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 << XSC_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 = 1ULL << 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..b5dc088fc
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/mr.c
@@ -0,0 +1,335 @@
+// 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->pdn = cpu_to_be32(to_xpd(pd)->pdn);
+	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,
+				    size_t 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;
+	size_t pas_size;
+	size_t inlen;
+	int err;
+
+	mr = kzalloc_obj(*mr, GFP_KERNEL);
+	if (!mr) {
+		err = -ENOMEM;
+		goto err_0;
+	}
+
+	if (npages > dev->xdev->caps.max_mtt) {
+		ibdev_err(&dev->ib_dev, "npages(%zu) exceeded.\n", npages);
+		err = -EINVAL;
+		goto err_1;
+	}
+
+	if (check_mul_overflow(npages, sizeof(*in->req.pas), &pas_size) ||
+	    check_add_overflow(pas_size, sizeof(*in), &inlen) ||
+	    inlen > INT_MAX) {
+		err = -EINVAL;
+		goto err_1;
+	}
+	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((u32)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;
+	size_t 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 %zu, 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;
+	size_t npages;
+	int err;
+
+	if (!IS_ENABLED(CONFIG_INFINIBAND_USER_MEM))
+		return ERR_PTR(-EOPNOTSUPP);
+
+	if (dmah)
+		return ERR_PTR(-EOPNOTSUPP);
+
+	if (length > dev->xdev->caps.max_mr_size) {
+		ibdev_err(&dev->ib_dev, "reg user dmabuf mr length(%llu) exceeded.\n",
+			  length);
+		return ERR_PTR(-EINVAL);
+	}
+
+	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;
+}
+
+int 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, XSC_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,
+			"failed to register the local dma mkey 0xFF (%d)\n", err);
+
+	return err;
+}
diff --git a/drivers/infiniband/hw/xsc/xsc_ib.h b/drivers/infiniband/hw/xsc/xsc_ib.h
index b91d4aa37..eb4d9e630 100644
--- a/drivers/infiniband/hw/xsc/xsc_ib.h
+++ b/drivers/infiniband/hw/xsc/xsc_ib.h
@@ -207,7 +207,7 @@ struct xsc_ib_mr {
 	struct ib_mr		ibmr;
 	struct xsc_core_mr	xmr;
 	struct ib_umem	       *umem;
-	int			npages;
+	u32			npages;
 };
 
 struct xsc_ib_dev {
diff --git a/drivers/net/ethernet/yunsilicon/xsc/common/driver.h b/drivers/net/ethernet/yunsilicon/xsc/common/driver.h
index 362099a7c..0c91574d6 100644
--- a/drivers/net/ethernet/yunsilicon/xsc/common/driver.h
+++ b/drivers/net/ethernet/yunsilicon/xsc/common/driver.h
@@ -63,6 +63,7 @@ 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);
+int xsc_reg_local_dma_mr(struct xsc_core_device *xdev);
 
 int xsc_core_alloc_pd(struct xsc_core_device *xdev, u32 *pdn);
 int xsc_core_dealloc_pd(struct xsc_core_device *xdev, u32 pdn);
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/cq.c b/drivers/net/ethernet/yunsilicon/xsc/pci/cq.c
index e3e13ef53..a964236bd 100644
--- a/drivers/net/ethernet/yunsilicon/xsc/pci/cq.c
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/cq.c
@@ -143,9 +143,9 @@ static int xsc_set_cq_context(struct xsc_core_device *xdev,
 
 	memset(&out, 0, sizeof(out));
 	err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
-	if (err || out.hdr.status) {
+	if (err) {
 		pci_err(xdev->pdev, "failed to set cq context\n");
-		return -EIO;
+		return err;
 	}
 
 	*pa_list_base = be32_to_cpu(out.cq_pa_list_base);
@@ -193,9 +193,9 @@ static int xsc_set_cq_buf_pa(struct xsc_core_device *xdev,
 		err = xsc_cmd_exec(xdev, in, in_len, &out, sizeof(out));
 		kvfree(in);
 
-		if (err || out.hdr.status) {
+		if (err) {
 			pci_err(xdev->pdev, "failed to set cq buf pa, cqn=%d\n", cqn);
-			return -EINVAL;
+			return err;
 		}
 	}
 
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..b07e8743f
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.c
@@ -0,0 +1,202 @@
+// 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;
+	}
+
+	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;
+
+	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) {
+		pci_err(xdev->pdev, "failed to set mpt, err = %d\n", err);
+		return err;
+	}
+
+	*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) {
+			pci_err(xdev->pdev, "failed to set mtt seg %d, err = %d\n",
+				seg_idx, err);
+			return err;
+		}
+
+		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) {
+		pci_err(xdev->pdev, "failed to dereg mr, err = %d\n", err);
+		return err;
+	}
+
+	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..e8e55bc4c
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.c
@@ -0,0 +1,49 @@
+// 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;
+
+	*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;
+
+	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 */
-- 
2.18.4
Thread (18 messages) 18 messages, 3 authors, last reply 3d ago
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help