// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021 - 2023, Shanghai Yunsilicon Technology Co., Ltd. * All rights reserved. */ #include #include #include #include #include #include "common/xsc_cmd.h" #include #include "ib_umem_ex.h" #include "xsc_ib.h" static void xsc_invalidate_umem(void *invalidation_cookie, struct ib_umem_ex *umem, unsigned long addr, size_t size); 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_mdev(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(sizeof(*mr), GFP_KERNEL); if (!mr) return ERR_PTR(-ENOMEM); return &mr->ibmr; in = kzalloc(sizeof(*in), GFP_KERNEL); if (!in) { err = -ENOMEM; goto err_free; } req = &in->req; req->acc = convert_access(acc) | XSC_ACCESS_MODE_PA; req->pdn = cpu_to_be32(to_mpd(pd)->pdn | XSC_MKEY_LEN64); req->va_base = 0; err = xsc_core_create_mkey(xdev, &mr->mmr); if (err) goto err_in; req->mkey = cpu_to_be32(mr->mmr.key); err = xsc_core_register_mr(xdev, &mr->mmr, in, sizeof(*in)); if (err) goto err_reg_mr; kfree(in); mr->ibmr.lkey = mr->mmr.key; mr->ibmr.rkey = mr->mmr.key; mr->umem = NULL; return &mr->ibmr; err_reg_mr: xsc_core_destroy_mkey(xdev, &mr->mmr); err_in: kfree(in); err_free: kfree(mr); return ERR_PTR(err); } void xsc_fill_pas(int npages, u64 *pas, __be64 *req_pas) { int i; for (i = 0; i < npages; i++) req_pas[i] = cpu_to_be64(pas[i]); } static struct xsc_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr, u64 length, struct ib_umem *umem, int npages, u64 *pas, int page_shift, int access_flags) { struct xsc_ib_dev *dev = to_mdev(pd->device); struct xsc_register_mr_mbox_in *in; struct xsc_ib_mr *mr; int inlen; int err; mr = kzalloc(sizeof(*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->mmr); if (err) { xsc_ib_warn(dev, "create mkey failed\n"); goto err_2; } xsc_fill_pas(npages, pas, in->req.pas); in->req.acc = convert_access(access_flags); in->req.pa_num = cpu_to_be32(npages); in->req.pdn = cpu_to_be32(to_mpd(pd)->pdn); in->req.va_base = cpu_to_be64(virt_addr); in->req.map_en = XSC_MPT_MAP_EN; in->req.len = cpu_to_be32((u32)length); in->req.page_mode = (page_shift == XSC_PAGE_SHIFT_4K ? XSC_PAGE_MODE_4K : (page_shift == XSC_PAGE_SHIFT_64K ? XSC_PAGE_MODE_64K : (page_shift == XSC_PAGE_SHIFT_2M ? XSC_PAGE_MODE_2M : XSC_PAGE_MODE_1G))); in->req.mkey = cpu_to_be32(mr->mmr.key); err = xsc_core_register_mr(dev->xdev, &mr->mmr, in, inlen); if (err) { xsc_ib_warn(dev, "register mr failed, err = %d\n", err); goto err_reg_mr; } mr->umem = umem; xsc_vfree(in); vfree(pas); xsc_ib_dbg(dev, "mkey = 0x%x\n", mr->mmr.key); return mr; err_reg_mr: xsc_core_destroy_mkey(dev->xdev, &mr->mmr); err_2: xsc_vfree(in); err_1: kfree(mr); err_0: vfree(pas); 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_udata *udata) { struct xsc_ib_dev *dev = to_mdev(pd->device); struct xsc_ib_mr *mr = NULL; struct ib_umem_ex *umem_ex; struct ib_umem *umem; int page_shift; int npages; u64 *pas; int err; int using_peer_mem = 0; struct ib_peer_memory_client *ib_peer_mem = NULL; struct xsc_ib_peer_id *xsc_ib_peer_id = NULL; xsc_ib_dbg(dev, "start 0x%llx, virt_addr 0x%llx, length 0x%llx\n", start, virt_addr, length); umem = ib_umem_get(&dev->ib_dev, start, length, access_flags); if (IS_ERR(umem)) { // check client peer memory u8 peer_exists = 0; umem_ex = ib_client_umem_get(pd->uobject->context, start, length, access_flags, 0, &peer_exists); if (!peer_exists) { xsc_ib_dbg(dev, "umem get failed\n"); return (void *)umem; } ib_peer_mem = umem_ex->ib_peer_mem; xsc_ib_peer_id = kzalloc(sizeof(*xsc_ib_peer_id), GFP_KERNEL); if (!xsc_ib_peer_id) { err = -ENOMEM; goto error; } init_completion(&xsc_ib_peer_id->comp); err = ib_client_umem_activate_invalidation_notifier(umem_ex, xsc_invalidate_umem, xsc_ib_peer_id); if (err) goto error; using_peer_mem = 1; } else { umem_ex = ib_umem_ex(umem); if (IS_ERR(umem_ex)) { err = -ENOMEM; goto error; } } umem = &umem_ex->umem; err = xsc_find_best_pgsz(umem, 0x40211000, start, &npages, &page_shift, &pas); if (err) { vfree(pas); pas = NULL; xsc_ib_warn(dev, "find best page size failed\n"); goto error; } if (!npages) { xsc_ib_warn(dev, "avoid zero region\n"); err = -EINVAL; goto error; } xsc_ib_dbg(dev, "npages %d, page_shift %d\n", npages, page_shift); mr = reg_create(pd, virt_addr, length, umem, npages, pas, page_shift, access_flags); if (IS_ERR(mr)) { err = PTR_ERR(mr); goto error; } xsc_ib_dbg(dev, "mkey 0x%x\n", mr->mmr.key); mr->umem = umem; mr->npages = npages; spin_lock(&dev->mr_lock); dev->xdev->dev_res->reg_pages += npages; spin_unlock(&dev->mr_lock); mr->ibmr.lkey = mr->mmr.key; mr->ibmr.rkey = mr->mmr.key; mr->ibmr.length = length; atomic_set(&mr->invalidated, 0); if (ib_peer_mem) { init_completion(&mr->invalidation_comp); xsc_ib_peer_id->mr = mr; mr->peer_id = xsc_ib_peer_id; complete(&xsc_ib_peer_id->comp); } return &mr->ibmr; error: if (xsc_ib_peer_id) { complete(&xsc_ib_peer_id->comp); kfree(xsc_ib_peer_id); xsc_ib_peer_id = NULL; } ib_umem_ex_release(umem_ex); return ERR_PTR(err); } xsc_ib_dereg_mr_def() { struct xsc_ib_dev *dev = to_mdev(ibmr->device); struct xsc_ib_mr *mr = to_mmr(ibmr); struct ib_umem *umem = mr->umem; struct ib_umem_ex *umem_ex = (struct ib_umem_ex *)umem; int npages = mr->npages; int err; xsc_ib_dbg(dev, "dereg mkey = 0x%x\n", mr->mmr.key); if (atomic_inc_return(&mr->invalidated) > 1) { /* In case there is inflight invalidation call pending for its termination */ wait_for_completion(&mr->invalidation_comp); kfree(mr); return 0; } if (mr->npages) { err = xsc_core_dereg_mr(dev->xdev, &mr->mmr); if (err) { xsc_ib_warn(dev, "failed to dereg mr 0x%x (%d)\n", mr->mmr.key, err); return err; } } err = xsc_core_destroy_mkey(dev->xdev, &mr->mmr); if (err) { xsc_ib_warn(dev, "failed to destroy mkey 0x%x (%d)\n", mr->mmr.key, err); return err; } if (umem_ex) { ib_umem_ex_release(umem_ex); spin_lock(&dev->mr_lock); dev->xdev->dev_res->reg_pages -= npages; spin_unlock(&dev->mr_lock); } kfree(mr); return 0; } static void xsc_invalidate_umem(void *invalidation_cookie, struct ib_umem_ex *umem, unsigned long addr, size_t size) { struct xsc_ib_mr *mr; struct xsc_ib_dev *dev; struct xsc_ib_peer_id *peer_id = (struct xsc_ib_peer_id *)invalidation_cookie; wait_for_completion(&peer_id->comp); if (!peer_id->mr) return; mr = peer_id->mr; /* This function is called under client peer lock so its resources are race protected */ if (atomic_inc_return(&mr->invalidated) > 1) { umem->invalidation_ctx->inflight_invalidation = 1; return; } umem->invalidation_ctx->peer_callback = 1; dev = to_mdev(mr->ibmr.device); xsc_core_destroy_mkey(dev->xdev, &mr->mmr); xsc_core_dereg_mr(dev->xdev, &mr->mmr); complete(&mr->invalidation_comp); } xsc_ib_alloc_mr_def() { struct xsc_ib_dev *dev = to_mdev(pd->device); struct xsc_ib_mr *mr; int err; mr = kzalloc(sizeof(*mr), GFP_KERNEL); if (!mr) return NULL; mr->npages = 0; mr->mmr.pd = to_mpd(pd)->pdn; mr->pas = kcalloc(max_num_sg, sizeof(__be64), GFP_KERNEL); if (!mr->pas) goto err_alloc; err = xsc_core_create_mkey(dev->xdev, &mr->mmr); if (err) goto err_create_mkey; mr->ibmr.lkey = mr->mmr.key; mr->ibmr.rkey = mr->mmr.key; mr->ibmr.device = &dev->ib_dev; return &mr->ibmr; err_create_mkey: kfree(mr->pas); err_alloc: kfree(mr); return NULL; } static int xsc_set_page(struct ib_mr *ibmr, u64 pa) { struct xsc_ib_mr *mmr = to_mmr(ibmr); mmr->pas[mmr->npages] = pa; mmr->npages++; return 0; } int xsc_ib_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg, int sg_nents, unsigned int *sg_offset) { struct xsc_ib_mr *mmr = to_mmr(ibmr); mmr->npages = 0; return ib_sg_to_pages(ibmr, sg, sg_nents, sg_offset, xsc_set_page); } int xsc_wr_reg_mr(struct xsc_ib_dev *dev, const struct ib_send_wr *wr) { const struct ib_reg_wr *reg_wr = container_of(wr, struct ib_reg_wr, wr); struct ib_mr *ibmr = reg_wr->mr; struct xsc_ib_mr *mmr = to_mmr(ibmr); struct xsc_register_mr_mbox_in *in; int inlen; int i; int err; __be64 *pas; inlen = sizeof(*in) + sizeof(__be64) * mmr->npages; in = kzalloc(inlen, GFP_ATOMIC); if (!in) return -ENOMEM; in->req.pdn = cpu_to_be32(mmr->mmr.pd); in->req.pa_num = cpu_to_be32(mmr->npages); in->req.len = cpu_to_be32(ibmr->length); in->req.mkey = cpu_to_be32(ibmr->rkey); in->req.acc = convert_access(reg_wr->access); in->req.page_mode = 0; in->req.map_en = XSC_MPT_MAP_EN; in->req.va_base = cpu_to_be64(ibmr->iova); pas = in->req.pas; for (i = 0; i < mmr->npages; i++) pas[i] = cpu_to_be64(mmr->pas[i]); err = xsc_core_register_mr(dev->xdev, &mmr->mmr, in, sizeof(*in)); kfree(in); return err; } int xsc_wr_invalidate_mr(struct xsc_ib_dev *dev, const struct ib_send_wr *wr) { struct xsc_core_mr mr; int err = 0; if (!wr) return -1; mr.key = wr->ex.invalidate_rkey; err = xsc_core_dereg_mr(dev->xdev, &mr); return err; } 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 = 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) xsc_core_err(dev, "\n"); }