18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2013-2018, Mellanox Technologies inc.  All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/mlx5/qp.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <rdma/ib_umem.h>
108c2ecf20Sopenharmony_ci#include <rdma/ib_user_verbs.h>
118c2ecf20Sopenharmony_ci#include "mlx5_ib.h"
128c2ecf20Sopenharmony_ci#include "srq.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic void *get_wqe(struct mlx5_ib_srq *srq, int n)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	return mlx5_frag_buf_get_wqe(&srq->fbc, n);
178c2ecf20Sopenharmony_ci}
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic void mlx5_ib_srq_event(struct mlx5_core_srq *srq, enum mlx5_event type)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct ib_event event;
228c2ecf20Sopenharmony_ci	struct ib_srq *ibsrq = &to_mibsrq(srq)->ibsrq;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	if (ibsrq->event_handler) {
258c2ecf20Sopenharmony_ci		event.device      = ibsrq->device;
268c2ecf20Sopenharmony_ci		event.element.srq = ibsrq;
278c2ecf20Sopenharmony_ci		switch (type) {
288c2ecf20Sopenharmony_ci		case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT:
298c2ecf20Sopenharmony_ci			event.event = IB_EVENT_SRQ_LIMIT_REACHED;
308c2ecf20Sopenharmony_ci			break;
318c2ecf20Sopenharmony_ci		case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR:
328c2ecf20Sopenharmony_ci			event.event = IB_EVENT_SRQ_ERR;
338c2ecf20Sopenharmony_ci			break;
348c2ecf20Sopenharmony_ci		default:
358c2ecf20Sopenharmony_ci			pr_warn("mlx5_ib: Unexpected event type %d on SRQ %06x\n",
368c2ecf20Sopenharmony_ci				type, srq->srqn);
378c2ecf20Sopenharmony_ci			return;
388c2ecf20Sopenharmony_ci		}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci		ibsrq->event_handler(&event, ibsrq->srq_context);
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
458c2ecf20Sopenharmony_ci			   struct mlx5_srq_attr *in,
468c2ecf20Sopenharmony_ci			   struct ib_udata *udata, int buf_size)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct mlx5_ib_dev *dev = to_mdev(pd->device);
498c2ecf20Sopenharmony_ci	struct mlx5_ib_create_srq ucmd = {};
508c2ecf20Sopenharmony_ci	struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
518c2ecf20Sopenharmony_ci		udata, struct mlx5_ib_ucontext, ibucontext);
528c2ecf20Sopenharmony_ci	size_t ucmdlen;
538c2ecf20Sopenharmony_ci	int err;
548c2ecf20Sopenharmony_ci	int npages;
558c2ecf20Sopenharmony_ci	int page_shift;
568c2ecf20Sopenharmony_ci	int ncont;
578c2ecf20Sopenharmony_ci	u32 offset;
588c2ecf20Sopenharmony_ci	u32 uidx = MLX5_IB_DEFAULT_UIDX;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	ucmdlen = min(udata->inlen, sizeof(ucmd));
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (ib_copy_from_udata(&ucmd, udata, ucmdlen)) {
638c2ecf20Sopenharmony_ci		mlx5_ib_dbg(dev, "failed copy udata\n");
648c2ecf20Sopenharmony_ci		return -EFAULT;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (ucmd.reserved0 || ucmd.reserved1)
688c2ecf20Sopenharmony_ci		return -EINVAL;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (udata->inlen > sizeof(ucmd) &&
718c2ecf20Sopenharmony_ci	    !ib_is_udata_cleared(udata, sizeof(ucmd),
728c2ecf20Sopenharmony_ci				 udata->inlen - sizeof(ucmd)))
738c2ecf20Sopenharmony_ci		return -EINVAL;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (in->type != IB_SRQT_BASIC) {
768c2ecf20Sopenharmony_ci		err = get_srq_user_index(ucontext, &ucmd, udata->inlen, &uidx);
778c2ecf20Sopenharmony_ci		if (err)
788c2ecf20Sopenharmony_ci			return err;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	srq->wq_sig = !!(ucmd.flags & MLX5_SRQ_FLAG_SIGNATURE);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	srq->umem = ib_umem_get(pd->device, ucmd.buf_addr, buf_size, 0);
848c2ecf20Sopenharmony_ci	if (IS_ERR(srq->umem)) {
858c2ecf20Sopenharmony_ci		mlx5_ib_dbg(dev, "failed umem get, size %d\n", buf_size);
868c2ecf20Sopenharmony_ci		err = PTR_ERR(srq->umem);
878c2ecf20Sopenharmony_ci		return err;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	mlx5_ib_cont_pages(srq->umem, ucmd.buf_addr, 0, &npages,
918c2ecf20Sopenharmony_ci			   &page_shift, &ncont, NULL);
928c2ecf20Sopenharmony_ci	err = mlx5_ib_get_buf_offset(ucmd.buf_addr, page_shift,
938c2ecf20Sopenharmony_ci				     &offset);
948c2ecf20Sopenharmony_ci	if (err) {
958c2ecf20Sopenharmony_ci		mlx5_ib_warn(dev, "bad offset\n");
968c2ecf20Sopenharmony_ci		goto err_umem;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	in->pas = kvcalloc(ncont, sizeof(*in->pas), GFP_KERNEL);
1008c2ecf20Sopenharmony_ci	if (!in->pas) {
1018c2ecf20Sopenharmony_ci		err = -ENOMEM;
1028c2ecf20Sopenharmony_ci		goto err_umem;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	mlx5_ib_populate_pas(dev, srq->umem, page_shift, in->pas, 0);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	err = mlx5_ib_db_map_user(ucontext, udata, ucmd.db_addr, &srq->db);
1088c2ecf20Sopenharmony_ci	if (err) {
1098c2ecf20Sopenharmony_ci		mlx5_ib_dbg(dev, "map doorbell failed\n");
1108c2ecf20Sopenharmony_ci		goto err_in;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	in->log_page_size = page_shift - MLX5_ADAPTER_PAGE_SHIFT;
1148c2ecf20Sopenharmony_ci	in->page_offset = offset;
1158c2ecf20Sopenharmony_ci	in->uid = (in->type != IB_SRQT_XRC) ?  to_mpd(pd)->uid : 0;
1168c2ecf20Sopenharmony_ci	if (MLX5_CAP_GEN(dev->mdev, cqe_version) == MLX5_CQE_VERSION_V1 &&
1178c2ecf20Sopenharmony_ci	    in->type != IB_SRQT_BASIC)
1188c2ecf20Sopenharmony_ci		in->user_index = uidx;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cierr_in:
1238c2ecf20Sopenharmony_ci	kvfree(in->pas);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cierr_umem:
1268c2ecf20Sopenharmony_ci	ib_umem_release(srq->umem);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return err;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq,
1328c2ecf20Sopenharmony_ci			     struct mlx5_srq_attr *in, int buf_size)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	int err;
1358c2ecf20Sopenharmony_ci	int i;
1368c2ecf20Sopenharmony_ci	struct mlx5_wqe_srq_next_seg *next;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	err = mlx5_db_alloc(dev->mdev, &srq->db);
1398c2ecf20Sopenharmony_ci	if (err) {
1408c2ecf20Sopenharmony_ci		mlx5_ib_warn(dev, "alloc dbell rec failed\n");
1418c2ecf20Sopenharmony_ci		return err;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (mlx5_frag_buf_alloc_node(dev->mdev, buf_size, &srq->buf,
1458c2ecf20Sopenharmony_ci				     dev->mdev->priv.numa_node)) {
1468c2ecf20Sopenharmony_ci		mlx5_ib_dbg(dev, "buf alloc failed\n");
1478c2ecf20Sopenharmony_ci		err = -ENOMEM;
1488c2ecf20Sopenharmony_ci		goto err_db;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	mlx5_init_fbc(srq->buf.frags, srq->msrq.wqe_shift, ilog2(srq->msrq.max),
1528c2ecf20Sopenharmony_ci		      &srq->fbc);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	srq->head    = 0;
1558c2ecf20Sopenharmony_ci	srq->tail    = srq->msrq.max - 1;
1568c2ecf20Sopenharmony_ci	srq->wqe_ctr = 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	for (i = 0; i < srq->msrq.max; i++) {
1598c2ecf20Sopenharmony_ci		next = get_wqe(srq, i);
1608c2ecf20Sopenharmony_ci		next->next_wqe_index =
1618c2ecf20Sopenharmony_ci			cpu_to_be16((i + 1) & (srq->msrq.max - 1));
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	mlx5_ib_dbg(dev, "srq->buf.page_shift = %d\n", srq->buf.page_shift);
1658c2ecf20Sopenharmony_ci	in->pas = kvcalloc(srq->buf.npages, sizeof(*in->pas), GFP_KERNEL);
1668c2ecf20Sopenharmony_ci	if (!in->pas) {
1678c2ecf20Sopenharmony_ci		err = -ENOMEM;
1688c2ecf20Sopenharmony_ci		goto err_buf;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci	mlx5_fill_page_frag_array(&srq->buf, in->pas);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	srq->wrid = kvmalloc_array(srq->msrq.max, sizeof(u64), GFP_KERNEL);
1738c2ecf20Sopenharmony_ci	if (!srq->wrid) {
1748c2ecf20Sopenharmony_ci		err = -ENOMEM;
1758c2ecf20Sopenharmony_ci		goto err_in;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci	srq->wq_sig = 0;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	in->log_page_size = srq->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT;
1808c2ecf20Sopenharmony_ci	if (MLX5_CAP_GEN(dev->mdev, cqe_version) == MLX5_CQE_VERSION_V1 &&
1818c2ecf20Sopenharmony_ci	    in->type != IB_SRQT_BASIC)
1828c2ecf20Sopenharmony_ci		in->user_index = MLX5_IB_DEFAULT_UIDX;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cierr_in:
1878c2ecf20Sopenharmony_ci	kvfree(in->pas);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cierr_buf:
1908c2ecf20Sopenharmony_ci	mlx5_frag_buf_free(dev->mdev, &srq->buf);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cierr_db:
1938c2ecf20Sopenharmony_ci	mlx5_db_free(dev->mdev, &srq->db);
1948c2ecf20Sopenharmony_ci	return err;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void destroy_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
1988c2ecf20Sopenharmony_ci			     struct ib_udata *udata)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	mlx5_ib_db_unmap_user(
2018c2ecf20Sopenharmony_ci		rdma_udata_to_drv_context(
2028c2ecf20Sopenharmony_ci			udata,
2038c2ecf20Sopenharmony_ci			struct mlx5_ib_ucontext,
2048c2ecf20Sopenharmony_ci			ibucontext),
2058c2ecf20Sopenharmony_ci		&srq->db);
2068c2ecf20Sopenharmony_ci	ib_umem_release(srq->umem);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic void destroy_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	kvfree(srq->wrid);
2138c2ecf20Sopenharmony_ci	mlx5_frag_buf_free(dev->mdev, &srq->buf);
2148c2ecf20Sopenharmony_ci	mlx5_db_free(dev->mdev, &srq->db);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ciint mlx5_ib_create_srq(struct ib_srq *ib_srq,
2188c2ecf20Sopenharmony_ci		       struct ib_srq_init_attr *init_attr,
2198c2ecf20Sopenharmony_ci		       struct ib_udata *udata)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct mlx5_ib_dev *dev = to_mdev(ib_srq->device);
2228c2ecf20Sopenharmony_ci	struct mlx5_ib_srq *srq = to_msrq(ib_srq);
2238c2ecf20Sopenharmony_ci	size_t desc_size;
2248c2ecf20Sopenharmony_ci	size_t buf_size;
2258c2ecf20Sopenharmony_ci	int err;
2268c2ecf20Sopenharmony_ci	struct mlx5_srq_attr in = {};
2278c2ecf20Sopenharmony_ci	__u32 max_srq_wqes = 1 << MLX5_CAP_GEN(dev->mdev, log_max_srq_sz);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	/* Sanity check SRQ size before proceeding */
2308c2ecf20Sopenharmony_ci	if (init_attr->attr.max_wr >= max_srq_wqes) {
2318c2ecf20Sopenharmony_ci		mlx5_ib_dbg(dev, "max_wr %d, cap %d\n",
2328c2ecf20Sopenharmony_ci			    init_attr->attr.max_wr,
2338c2ecf20Sopenharmony_ci			    max_srq_wqes);
2348c2ecf20Sopenharmony_ci		return -EINVAL;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	mutex_init(&srq->mutex);
2388c2ecf20Sopenharmony_ci	spin_lock_init(&srq->lock);
2398c2ecf20Sopenharmony_ci	srq->msrq.max    = roundup_pow_of_two(init_attr->attr.max_wr + 1);
2408c2ecf20Sopenharmony_ci	srq->msrq.max_gs = init_attr->attr.max_sge;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	desc_size = sizeof(struct mlx5_wqe_srq_next_seg) +
2438c2ecf20Sopenharmony_ci		    srq->msrq.max_gs * sizeof(struct mlx5_wqe_data_seg);
2448c2ecf20Sopenharmony_ci	if (desc_size == 0 || srq->msrq.max_gs > desc_size)
2458c2ecf20Sopenharmony_ci		return -EINVAL;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	desc_size = roundup_pow_of_two(desc_size);
2488c2ecf20Sopenharmony_ci	desc_size = max_t(size_t, 32, desc_size);
2498c2ecf20Sopenharmony_ci	if (desc_size < sizeof(struct mlx5_wqe_srq_next_seg))
2508c2ecf20Sopenharmony_ci		return -EINVAL;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	srq->msrq.max_avail_gather = (desc_size - sizeof(struct mlx5_wqe_srq_next_seg)) /
2538c2ecf20Sopenharmony_ci		sizeof(struct mlx5_wqe_data_seg);
2548c2ecf20Sopenharmony_ci	srq->msrq.wqe_shift = ilog2(desc_size);
2558c2ecf20Sopenharmony_ci	buf_size = srq->msrq.max * desc_size;
2568c2ecf20Sopenharmony_ci	if (buf_size < desc_size)
2578c2ecf20Sopenharmony_ci		return -EINVAL;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	in.type = init_attr->srq_type;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (udata)
2628c2ecf20Sopenharmony_ci		err = create_srq_user(ib_srq->pd, srq, &in, udata, buf_size);
2638c2ecf20Sopenharmony_ci	else
2648c2ecf20Sopenharmony_ci		err = create_srq_kernel(dev, srq, &in, buf_size);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (err) {
2678c2ecf20Sopenharmony_ci		mlx5_ib_warn(dev, "create srq %s failed, err %d\n",
2688c2ecf20Sopenharmony_ci			     udata ? "user" : "kernel", err);
2698c2ecf20Sopenharmony_ci		return err;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	in.log_size = ilog2(srq->msrq.max);
2738c2ecf20Sopenharmony_ci	in.wqe_shift = srq->msrq.wqe_shift - 4;
2748c2ecf20Sopenharmony_ci	if (srq->wq_sig)
2758c2ecf20Sopenharmony_ci		in.flags |= MLX5_SRQ_FLAG_WQ_SIG;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (init_attr->srq_type == IB_SRQT_XRC && init_attr->ext.xrc.xrcd)
2788c2ecf20Sopenharmony_ci		in.xrcd = to_mxrcd(init_attr->ext.xrc.xrcd)->xrcdn;
2798c2ecf20Sopenharmony_ci	else
2808c2ecf20Sopenharmony_ci		in.xrcd = dev->devr.xrcdn0;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (init_attr->srq_type == IB_SRQT_TM) {
2838c2ecf20Sopenharmony_ci		in.tm_log_list_size =
2848c2ecf20Sopenharmony_ci			ilog2(init_attr->ext.tag_matching.max_num_tags) + 1;
2858c2ecf20Sopenharmony_ci		if (in.tm_log_list_size >
2868c2ecf20Sopenharmony_ci		    MLX5_CAP_GEN(dev->mdev, log_tag_matching_list_sz)) {
2878c2ecf20Sopenharmony_ci			mlx5_ib_dbg(dev, "TM SRQ max_num_tags exceeding limit\n");
2888c2ecf20Sopenharmony_ci			err = -EINVAL;
2898c2ecf20Sopenharmony_ci			goto err_usr_kern_srq;
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci		in.flags |= MLX5_SRQ_FLAG_RNDV;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (ib_srq_has_cq(init_attr->srq_type))
2958c2ecf20Sopenharmony_ci		in.cqn = to_mcq(init_attr->ext.cq)->mcq.cqn;
2968c2ecf20Sopenharmony_ci	else
2978c2ecf20Sopenharmony_ci		in.cqn = to_mcq(dev->devr.c0)->mcq.cqn;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	in.pd = to_mpd(ib_srq->pd)->pdn;
3008c2ecf20Sopenharmony_ci	in.db_record = srq->db.dma;
3018c2ecf20Sopenharmony_ci	err = mlx5_cmd_create_srq(dev, &srq->msrq, &in);
3028c2ecf20Sopenharmony_ci	kvfree(in.pas);
3038c2ecf20Sopenharmony_ci	if (err) {
3048c2ecf20Sopenharmony_ci		mlx5_ib_dbg(dev, "create SRQ failed, err %d\n", err);
3058c2ecf20Sopenharmony_ci		goto err_usr_kern_srq;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	mlx5_ib_dbg(dev, "create SRQ with srqn 0x%x\n", srq->msrq.srqn);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	srq->msrq.event = mlx5_ib_srq_event;
3118c2ecf20Sopenharmony_ci	srq->ibsrq.ext.xrc.srq_num = srq->msrq.srqn;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (udata) {
3148c2ecf20Sopenharmony_ci		struct mlx5_ib_create_srq_resp resp = {
3158c2ecf20Sopenharmony_ci			.srqn = srq->msrq.srqn,
3168c2ecf20Sopenharmony_ci		};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		if (ib_copy_to_udata(udata, &resp, min(udata->outlen,
3198c2ecf20Sopenharmony_ci				     sizeof(resp)))) {
3208c2ecf20Sopenharmony_ci			mlx5_ib_dbg(dev, "copy to user failed\n");
3218c2ecf20Sopenharmony_ci			err = -EFAULT;
3228c2ecf20Sopenharmony_ci			goto err_core;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	init_attr->attr.max_wr = srq->msrq.max - 1;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return 0;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cierr_core:
3318c2ecf20Sopenharmony_ci	mlx5_cmd_destroy_srq(dev, &srq->msrq);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cierr_usr_kern_srq:
3348c2ecf20Sopenharmony_ci	if (udata)
3358c2ecf20Sopenharmony_ci		destroy_srq_user(ib_srq->pd, srq, udata);
3368c2ecf20Sopenharmony_ci	else
3378c2ecf20Sopenharmony_ci		destroy_srq_kernel(dev, srq);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return err;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ciint mlx5_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
3438c2ecf20Sopenharmony_ci		       enum ib_srq_attr_mask attr_mask, struct ib_udata *udata)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct mlx5_ib_dev *dev = to_mdev(ibsrq->device);
3468c2ecf20Sopenharmony_ci	struct mlx5_ib_srq *srq = to_msrq(ibsrq);
3478c2ecf20Sopenharmony_ci	int ret;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* We don't support resizing SRQs yet */
3508c2ecf20Sopenharmony_ci	if (attr_mask & IB_SRQ_MAX_WR)
3518c2ecf20Sopenharmony_ci		return -EINVAL;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (attr_mask & IB_SRQ_LIMIT) {
3548c2ecf20Sopenharmony_ci		if (attr->srq_limit >= srq->msrq.max)
3558c2ecf20Sopenharmony_ci			return -EINVAL;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		mutex_lock(&srq->mutex);
3588c2ecf20Sopenharmony_ci		ret = mlx5_cmd_arm_srq(dev, &srq->msrq, attr->srq_limit, 1);
3598c2ecf20Sopenharmony_ci		mutex_unlock(&srq->mutex);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		if (ret)
3628c2ecf20Sopenharmony_ci			return ret;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return 0;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ciint mlx5_ib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct mlx5_ib_dev *dev = to_mdev(ibsrq->device);
3718c2ecf20Sopenharmony_ci	struct mlx5_ib_srq *srq = to_msrq(ibsrq);
3728c2ecf20Sopenharmony_ci	int ret;
3738c2ecf20Sopenharmony_ci	struct mlx5_srq_attr *out;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	out = kzalloc(sizeof(*out), GFP_KERNEL);
3768c2ecf20Sopenharmony_ci	if (!out)
3778c2ecf20Sopenharmony_ci		return -ENOMEM;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	ret = mlx5_cmd_query_srq(dev, &srq->msrq, out);
3808c2ecf20Sopenharmony_ci	if (ret)
3818c2ecf20Sopenharmony_ci		goto out_box;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	srq_attr->srq_limit = out->lwm;
3848c2ecf20Sopenharmony_ci	srq_attr->max_wr    = srq->msrq.max - 1;
3858c2ecf20Sopenharmony_ci	srq_attr->max_sge   = srq->msrq.max_gs;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ciout_box:
3888c2ecf20Sopenharmony_ci	kfree(out);
3898c2ecf20Sopenharmony_ci	return ret;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ciint mlx5_ib_destroy_srq(struct ib_srq *srq, struct ib_udata *udata)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct mlx5_ib_dev *dev = to_mdev(srq->device);
3958c2ecf20Sopenharmony_ci	struct mlx5_ib_srq *msrq = to_msrq(srq);
3968c2ecf20Sopenharmony_ci	int ret;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	ret = mlx5_cmd_destroy_srq(dev, &msrq->msrq);
3998c2ecf20Sopenharmony_ci	if (ret)
4008c2ecf20Sopenharmony_ci		return ret;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	if (udata)
4038c2ecf20Sopenharmony_ci		destroy_srq_user(srq->pd, msrq, udata);
4048c2ecf20Sopenharmony_ci	else
4058c2ecf20Sopenharmony_ci		destroy_srq_kernel(dev, msrq);
4068c2ecf20Sopenharmony_ci	return 0;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_civoid mlx5_ib_free_srq_wqe(struct mlx5_ib_srq *srq, int wqe_index)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct mlx5_wqe_srq_next_seg *next;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	/* always called with interrupts disabled. */
4148c2ecf20Sopenharmony_ci	spin_lock(&srq->lock);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	next = get_wqe(srq, srq->tail);
4178c2ecf20Sopenharmony_ci	next->next_wqe_index = cpu_to_be16(wqe_index);
4188c2ecf20Sopenharmony_ci	srq->tail = wqe_index;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	spin_unlock(&srq->lock);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ciint mlx5_ib_post_srq_recv(struct ib_srq *ibsrq, const struct ib_recv_wr *wr,
4248c2ecf20Sopenharmony_ci			  const struct ib_recv_wr **bad_wr)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct mlx5_ib_srq *srq = to_msrq(ibsrq);
4278c2ecf20Sopenharmony_ci	struct mlx5_wqe_srq_next_seg *next;
4288c2ecf20Sopenharmony_ci	struct mlx5_wqe_data_seg *scat;
4298c2ecf20Sopenharmony_ci	struct mlx5_ib_dev *dev = to_mdev(ibsrq->device);
4308c2ecf20Sopenharmony_ci	struct mlx5_core_dev *mdev = dev->mdev;
4318c2ecf20Sopenharmony_ci	unsigned long flags;
4328c2ecf20Sopenharmony_ci	int err = 0;
4338c2ecf20Sopenharmony_ci	int nreq;
4348c2ecf20Sopenharmony_ci	int i;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&srq->lock, flags);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
4398c2ecf20Sopenharmony_ci		err = -EIO;
4408c2ecf20Sopenharmony_ci		*bad_wr = wr;
4418c2ecf20Sopenharmony_ci		goto out;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	for (nreq = 0; wr; nreq++, wr = wr->next) {
4458c2ecf20Sopenharmony_ci		if (unlikely(wr->num_sge > srq->msrq.max_gs)) {
4468c2ecf20Sopenharmony_ci			err = -EINVAL;
4478c2ecf20Sopenharmony_ci			*bad_wr = wr;
4488c2ecf20Sopenharmony_ci			break;
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		if (unlikely(srq->head == srq->tail)) {
4528c2ecf20Sopenharmony_ci			err = -ENOMEM;
4538c2ecf20Sopenharmony_ci			*bad_wr = wr;
4548c2ecf20Sopenharmony_ci			break;
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		srq->wrid[srq->head] = wr->wr_id;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		next      = get_wqe(srq, srq->head);
4608c2ecf20Sopenharmony_ci		srq->head = be16_to_cpu(next->next_wqe_index);
4618c2ecf20Sopenharmony_ci		scat      = (struct mlx5_wqe_data_seg *)(next + 1);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		for (i = 0; i < wr->num_sge; i++) {
4648c2ecf20Sopenharmony_ci			scat[i].byte_count = cpu_to_be32(wr->sg_list[i].length);
4658c2ecf20Sopenharmony_ci			scat[i].lkey       = cpu_to_be32(wr->sg_list[i].lkey);
4668c2ecf20Sopenharmony_ci			scat[i].addr       = cpu_to_be64(wr->sg_list[i].addr);
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		if (i < srq->msrq.max_avail_gather) {
4708c2ecf20Sopenharmony_ci			scat[i].byte_count = 0;
4718c2ecf20Sopenharmony_ci			scat[i].lkey       = cpu_to_be32(MLX5_INVALID_LKEY);
4728c2ecf20Sopenharmony_ci			scat[i].addr       = 0;
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (likely(nreq)) {
4778c2ecf20Sopenharmony_ci		srq->wqe_ctr += nreq;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		/* Make sure that descriptors are written before
4808c2ecf20Sopenharmony_ci		 * doorbell record.
4818c2ecf20Sopenharmony_ci		 */
4828c2ecf20Sopenharmony_ci		wmb();
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		*srq->db.db = cpu_to_be32(srq->wqe_ctr);
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ciout:
4878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&srq->lock, flags);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	return err;
4908c2ecf20Sopenharmony_ci}
491