162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved. 462306a36Sopenharmony_ci * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/vmalloc.h> 862306a36Sopenharmony_ci#include "rxe.h" 962306a36Sopenharmony_ci#include "rxe_queue.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ciint rxe_srq_chk_init(struct rxe_dev *rxe, struct ib_srq_init_attr *init) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci struct ib_srq_attr *attr = &init->attr; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci if (attr->max_wr > rxe->attr.max_srq_wr) { 1662306a36Sopenharmony_ci rxe_dbg_dev(rxe, "max_wr(%d) > max_srq_wr(%d)\n", 1762306a36Sopenharmony_ci attr->max_wr, rxe->attr.max_srq_wr); 1862306a36Sopenharmony_ci goto err1; 1962306a36Sopenharmony_ci } 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci if (attr->max_wr <= 0) { 2262306a36Sopenharmony_ci rxe_dbg_dev(rxe, "max_wr(%d) <= 0\n", attr->max_wr); 2362306a36Sopenharmony_ci goto err1; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (attr->max_wr < RXE_MIN_SRQ_WR) 2762306a36Sopenharmony_ci attr->max_wr = RXE_MIN_SRQ_WR; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (attr->max_sge > rxe->attr.max_srq_sge) { 3062306a36Sopenharmony_ci rxe_dbg_dev(rxe, "max_sge(%d) > max_srq_sge(%d)\n", 3162306a36Sopenharmony_ci attr->max_sge, rxe->attr.max_srq_sge); 3262306a36Sopenharmony_ci goto err1; 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (attr->max_sge < RXE_MIN_SRQ_SGE) 3662306a36Sopenharmony_ci attr->max_sge = RXE_MIN_SRQ_SGE; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cierr1: 4162306a36Sopenharmony_ci return -EINVAL; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciint rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, 4562306a36Sopenharmony_ci struct ib_srq_init_attr *init, struct ib_udata *udata, 4662306a36Sopenharmony_ci struct rxe_create_srq_resp __user *uresp) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct rxe_queue *q; 4962306a36Sopenharmony_ci int wqe_size; 5062306a36Sopenharmony_ci int err; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci srq->ibsrq.event_handler = init->event_handler; 5362306a36Sopenharmony_ci srq->ibsrq.srq_context = init->srq_context; 5462306a36Sopenharmony_ci srq->limit = init->attr.srq_limit; 5562306a36Sopenharmony_ci srq->srq_num = srq->elem.index; 5662306a36Sopenharmony_ci srq->rq.max_wr = init->attr.max_wr; 5762306a36Sopenharmony_ci srq->rq.max_sge = init->attr.max_sge; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci wqe_size = sizeof(struct rxe_recv_wqe) + 6062306a36Sopenharmony_ci srq->rq.max_sge*sizeof(struct ib_sge); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci spin_lock_init(&srq->rq.producer_lock); 6362306a36Sopenharmony_ci spin_lock_init(&srq->rq.consumer_lock); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci q = rxe_queue_init(rxe, &srq->rq.max_wr, wqe_size, 6662306a36Sopenharmony_ci QUEUE_TYPE_FROM_CLIENT); 6762306a36Sopenharmony_ci if (!q) { 6862306a36Sopenharmony_ci rxe_dbg_srq(srq, "Unable to allocate queue\n"); 6962306a36Sopenharmony_ci err = -ENOMEM; 7062306a36Sopenharmony_ci goto err_out; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf, 7462306a36Sopenharmony_ci q->buf_size, &q->ip); 7562306a36Sopenharmony_ci if (err) { 7662306a36Sopenharmony_ci rxe_dbg_srq(srq, "Unable to init mmap info for caller\n"); 7762306a36Sopenharmony_ci goto err_free; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci srq->rq.queue = q; 8162306a36Sopenharmony_ci init->attr.max_wr = srq->rq.max_wr; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (uresp) { 8462306a36Sopenharmony_ci if (copy_to_user(&uresp->srq_num, &srq->srq_num, 8562306a36Sopenharmony_ci sizeof(uresp->srq_num))) { 8662306a36Sopenharmony_ci rxe_queue_cleanup(q); 8762306a36Sopenharmony_ci return -EFAULT; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cierr_free: 9462306a36Sopenharmony_ci vfree(q->buf); 9562306a36Sopenharmony_ci kfree(q); 9662306a36Sopenharmony_cierr_out: 9762306a36Sopenharmony_ci return err; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciint rxe_srq_chk_attr(struct rxe_dev *rxe, struct rxe_srq *srq, 10162306a36Sopenharmony_ci struct ib_srq_attr *attr, enum ib_srq_attr_mask mask) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci if (srq->error) { 10462306a36Sopenharmony_ci rxe_dbg_srq(srq, "in error state\n"); 10562306a36Sopenharmony_ci goto err1; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (mask & IB_SRQ_MAX_WR) { 10962306a36Sopenharmony_ci if (attr->max_wr > rxe->attr.max_srq_wr) { 11062306a36Sopenharmony_ci rxe_dbg_srq(srq, "max_wr(%d) > max_srq_wr(%d)\n", 11162306a36Sopenharmony_ci attr->max_wr, rxe->attr.max_srq_wr); 11262306a36Sopenharmony_ci goto err1; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (attr->max_wr <= 0) { 11662306a36Sopenharmony_ci rxe_dbg_srq(srq, "max_wr(%d) <= 0\n", attr->max_wr); 11762306a36Sopenharmony_ci goto err1; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (srq->limit && (attr->max_wr < srq->limit)) { 12162306a36Sopenharmony_ci rxe_dbg_srq(srq, "max_wr (%d) < srq->limit (%d)\n", 12262306a36Sopenharmony_ci attr->max_wr, srq->limit); 12362306a36Sopenharmony_ci goto err1; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (attr->max_wr < RXE_MIN_SRQ_WR) 12762306a36Sopenharmony_ci attr->max_wr = RXE_MIN_SRQ_WR; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (mask & IB_SRQ_LIMIT) { 13162306a36Sopenharmony_ci if (attr->srq_limit > rxe->attr.max_srq_wr) { 13262306a36Sopenharmony_ci rxe_dbg_srq(srq, "srq_limit(%d) > max_srq_wr(%d)\n", 13362306a36Sopenharmony_ci attr->srq_limit, rxe->attr.max_srq_wr); 13462306a36Sopenharmony_ci goto err1; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (attr->srq_limit > srq->rq.queue->buf->index_mask) { 13862306a36Sopenharmony_ci rxe_dbg_srq(srq, "srq_limit (%d) > cur limit(%d)\n", 13962306a36Sopenharmony_ci attr->srq_limit, 14062306a36Sopenharmony_ci srq->rq.queue->buf->index_mask); 14162306a36Sopenharmony_ci goto err1; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cierr1: 14862306a36Sopenharmony_ci return -EINVAL; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciint rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, 15262306a36Sopenharmony_ci struct ib_srq_attr *attr, enum ib_srq_attr_mask mask, 15362306a36Sopenharmony_ci struct rxe_modify_srq_cmd *ucmd, struct ib_udata *udata) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct rxe_queue *q = srq->rq.queue; 15662306a36Sopenharmony_ci struct mminfo __user *mi = NULL; 15762306a36Sopenharmony_ci int wqe_size; 15862306a36Sopenharmony_ci int err; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (mask & IB_SRQ_MAX_WR) { 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * This is completely screwed up, the response is supposed to 16362306a36Sopenharmony_ci * be in the outbuf not like this. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci mi = u64_to_user_ptr(ucmd->mmap_info_addr); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci wqe_size = sizeof(struct rxe_recv_wqe) + 16862306a36Sopenharmony_ci srq->rq.max_sge*sizeof(struct ib_sge); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci err = rxe_queue_resize(q, &attr->max_wr, wqe_size, 17162306a36Sopenharmony_ci udata, mi, &srq->rq.producer_lock, 17262306a36Sopenharmony_ci &srq->rq.consumer_lock); 17362306a36Sopenharmony_ci if (err) 17462306a36Sopenharmony_ci goto err_free; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci srq->rq.max_wr = attr->max_wr; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (mask & IB_SRQ_LIMIT) 18062306a36Sopenharmony_ci srq->limit = attr->srq_limit; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cierr_free: 18562306a36Sopenharmony_ci rxe_queue_cleanup(q); 18662306a36Sopenharmony_ci srq->rq.queue = NULL; 18762306a36Sopenharmony_ci return err; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid rxe_srq_cleanup(struct rxe_pool_elem *elem) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct rxe_srq *srq = container_of(elem, typeof(*srq), elem); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (srq->pd) 19562306a36Sopenharmony_ci rxe_put(srq->pd); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (srq->rq.queue) 19862306a36Sopenharmony_ci rxe_queue_cleanup(srq->rq.queue); 19962306a36Sopenharmony_ci} 200