162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2005 Cisco Systems. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/slab.h> 3462306a36Sopenharmony_ci#include <linux/string.h> 3562306a36Sopenharmony_ci#include <linux/sched.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <asm/io.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <rdma/uverbs_ioctl.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "mthca_dev.h" 4262306a36Sopenharmony_ci#include "mthca_cmd.h" 4362306a36Sopenharmony_ci#include "mthca_memfree.h" 4462306a36Sopenharmony_ci#include "mthca_wqe.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cienum { 4762306a36Sopenharmony_ci MTHCA_MAX_DIRECT_SRQ_SIZE = 4 * PAGE_SIZE 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct mthca_tavor_srq_context { 5162306a36Sopenharmony_ci __be64 wqe_base_ds; /* low 6 bits is descriptor size */ 5262306a36Sopenharmony_ci __be32 state_pd; 5362306a36Sopenharmony_ci __be32 lkey; 5462306a36Sopenharmony_ci __be32 uar; 5562306a36Sopenharmony_ci __be16 limit_watermark; 5662306a36Sopenharmony_ci __be16 wqe_cnt; 5762306a36Sopenharmony_ci u32 reserved[2]; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct mthca_arbel_srq_context { 6162306a36Sopenharmony_ci __be32 state_logsize_srqn; 6262306a36Sopenharmony_ci __be32 lkey; 6362306a36Sopenharmony_ci __be32 db_index; 6462306a36Sopenharmony_ci __be32 logstride_usrpage; 6562306a36Sopenharmony_ci __be64 wqe_base; 6662306a36Sopenharmony_ci __be32 eq_pd; 6762306a36Sopenharmony_ci __be16 limit_watermark; 6862306a36Sopenharmony_ci __be16 wqe_cnt; 6962306a36Sopenharmony_ci u16 reserved1; 7062306a36Sopenharmony_ci __be16 wqe_counter; 7162306a36Sopenharmony_ci u32 reserved2[3]; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void *get_wqe(struct mthca_srq *srq, int n) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci if (srq->is_direct) 7762306a36Sopenharmony_ci return srq->queue.direct.buf + (n << srq->wqe_shift); 7862306a36Sopenharmony_ci else 7962306a36Sopenharmony_ci return srq->queue.page_list[(n << srq->wqe_shift) >> PAGE_SHIFT].buf + 8062306a36Sopenharmony_ci ((n << srq->wqe_shift) & (PAGE_SIZE - 1)); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * Return a pointer to the location within a WQE that we're using as a 8562306a36Sopenharmony_ci * link when the WQE is in the free list. We use the imm field 8662306a36Sopenharmony_ci * because in the Tavor case, posting a WQE may overwrite the next 8762306a36Sopenharmony_ci * segment of the previous WQE, but a receive WQE will never touch the 8862306a36Sopenharmony_ci * imm field. This avoids corrupting our free list if the previous 8962306a36Sopenharmony_ci * WQE has already completed and been put on the free list when we 9062306a36Sopenharmony_ci * post the next WQE. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic inline int *wqe_to_link(void *wqe) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci return (int *) (wqe + offsetof(struct mthca_next_seg, imm)); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void mthca_tavor_init_srq_context(struct mthca_dev *dev, 9862306a36Sopenharmony_ci struct mthca_pd *pd, 9962306a36Sopenharmony_ci struct mthca_srq *srq, 10062306a36Sopenharmony_ci struct mthca_tavor_srq_context *context, 10162306a36Sopenharmony_ci struct ib_udata *udata) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct mthca_ucontext *ucontext = rdma_udata_to_drv_context( 10462306a36Sopenharmony_ci udata, struct mthca_ucontext, ibucontext); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci memset(context, 0, sizeof *context); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci context->wqe_base_ds = cpu_to_be64(1 << (srq->wqe_shift - 4)); 10962306a36Sopenharmony_ci context->state_pd = cpu_to_be32(pd->pd_num); 11062306a36Sopenharmony_ci context->lkey = cpu_to_be32(srq->mr.ibmr.lkey); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (udata) 11362306a36Sopenharmony_ci context->uar = cpu_to_be32(ucontext->uar.index); 11462306a36Sopenharmony_ci else 11562306a36Sopenharmony_ci context->uar = cpu_to_be32(dev->driver_uar.index); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void mthca_arbel_init_srq_context(struct mthca_dev *dev, 11962306a36Sopenharmony_ci struct mthca_pd *pd, 12062306a36Sopenharmony_ci struct mthca_srq *srq, 12162306a36Sopenharmony_ci struct mthca_arbel_srq_context *context, 12262306a36Sopenharmony_ci struct ib_udata *udata) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct mthca_ucontext *ucontext = rdma_udata_to_drv_context( 12562306a36Sopenharmony_ci udata, struct mthca_ucontext, ibucontext); 12662306a36Sopenharmony_ci int logsize, max; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci memset(context, 0, sizeof *context); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * Put max in a temporary variable to work around gcc bug 13262306a36Sopenharmony_ci * triggered by ilog2() on sparc64. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci max = srq->max; 13562306a36Sopenharmony_ci logsize = ilog2(max); 13662306a36Sopenharmony_ci context->state_logsize_srqn = cpu_to_be32(logsize << 24 | srq->srqn); 13762306a36Sopenharmony_ci context->lkey = cpu_to_be32(srq->mr.ibmr.lkey); 13862306a36Sopenharmony_ci context->db_index = cpu_to_be32(srq->db_index); 13962306a36Sopenharmony_ci context->logstride_usrpage = cpu_to_be32((srq->wqe_shift - 4) << 29); 14062306a36Sopenharmony_ci if (udata) 14162306a36Sopenharmony_ci context->logstride_usrpage |= cpu_to_be32(ucontext->uar.index); 14262306a36Sopenharmony_ci else 14362306a36Sopenharmony_ci context->logstride_usrpage |= cpu_to_be32(dev->driver_uar.index); 14462306a36Sopenharmony_ci context->eq_pd = cpu_to_be32(MTHCA_EQ_ASYNC << 24 | pd->pd_num); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void mthca_free_srq_buf(struct mthca_dev *dev, struct mthca_srq *srq) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci mthca_buf_free(dev, srq->max << srq->wqe_shift, &srq->queue, 15062306a36Sopenharmony_ci srq->is_direct, &srq->mr); 15162306a36Sopenharmony_ci kfree(srq->wrid); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int mthca_alloc_srq_buf(struct mthca_dev *dev, struct mthca_pd *pd, 15562306a36Sopenharmony_ci struct mthca_srq *srq, struct ib_udata *udata) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct mthca_data_seg *scatter; 15862306a36Sopenharmony_ci void *wqe; 15962306a36Sopenharmony_ci int err; 16062306a36Sopenharmony_ci int i; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (udata) 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci srq->wrid = kmalloc_array(srq->max, sizeof(u64), GFP_KERNEL); 16662306a36Sopenharmony_ci if (!srq->wrid) 16762306a36Sopenharmony_ci return -ENOMEM; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci err = mthca_buf_alloc(dev, srq->max << srq->wqe_shift, 17062306a36Sopenharmony_ci MTHCA_MAX_DIRECT_SRQ_SIZE, 17162306a36Sopenharmony_ci &srq->queue, &srq->is_direct, pd, 1, &srq->mr); 17262306a36Sopenharmony_ci if (err) { 17362306a36Sopenharmony_ci kfree(srq->wrid); 17462306a36Sopenharmony_ci return err; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * Now initialize the SRQ buffer so that all of the WQEs are 17962306a36Sopenharmony_ci * linked into the list of free WQEs. In addition, set the 18062306a36Sopenharmony_ci * scatter list L_Keys to the sentry value of 0x100. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci for (i = 0; i < srq->max; ++i) { 18362306a36Sopenharmony_ci struct mthca_next_seg *next; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci next = wqe = get_wqe(srq, i); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (i < srq->max - 1) { 18862306a36Sopenharmony_ci *wqe_to_link(wqe) = i + 1; 18962306a36Sopenharmony_ci next->nda_op = htonl(((i + 1) << srq->wqe_shift) | 1); 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci *wqe_to_link(wqe) = -1; 19262306a36Sopenharmony_ci next->nda_op = 0; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci for (scatter = wqe + sizeof (struct mthca_next_seg); 19662306a36Sopenharmony_ci (void *) scatter < wqe + (1 << srq->wqe_shift); 19762306a36Sopenharmony_ci ++scatter) 19862306a36Sopenharmony_ci scatter->lkey = cpu_to_be32(MTHCA_INVAL_LKEY); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci srq->last = get_wqe(srq, srq->max - 1); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciint mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd, 20762306a36Sopenharmony_ci struct ib_srq_attr *attr, struct mthca_srq *srq, 20862306a36Sopenharmony_ci struct ib_udata *udata) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct mthca_mailbox *mailbox; 21162306a36Sopenharmony_ci int ds; 21262306a36Sopenharmony_ci int err; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Sanity check SRQ size before proceeding */ 21562306a36Sopenharmony_ci if (attr->max_wr > dev->limits.max_srq_wqes || 21662306a36Sopenharmony_ci attr->max_sge > dev->limits.max_srq_sge) 21762306a36Sopenharmony_ci return -EINVAL; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci srq->max = attr->max_wr; 22062306a36Sopenharmony_ci srq->max_gs = attr->max_sge; 22162306a36Sopenharmony_ci srq->counter = 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 22462306a36Sopenharmony_ci srq->max = roundup_pow_of_two(srq->max + 1); 22562306a36Sopenharmony_ci else 22662306a36Sopenharmony_ci srq->max = srq->max + 1; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ds = max(64UL, 22962306a36Sopenharmony_ci roundup_pow_of_two(sizeof (struct mthca_next_seg) + 23062306a36Sopenharmony_ci srq->max_gs * sizeof (struct mthca_data_seg))); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (!mthca_is_memfree(dev) && (ds > dev->limits.max_desc_sz)) 23362306a36Sopenharmony_ci return -EINVAL; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci srq->wqe_shift = ilog2(ds); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci srq->srqn = mthca_alloc(&dev->srq_table.alloc); 23862306a36Sopenharmony_ci if (srq->srqn == -1) 23962306a36Sopenharmony_ci return -ENOMEM; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (mthca_is_memfree(dev)) { 24262306a36Sopenharmony_ci err = mthca_table_get(dev, dev->srq_table.table, srq->srqn); 24362306a36Sopenharmony_ci if (err) 24462306a36Sopenharmony_ci goto err_out; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!udata) { 24762306a36Sopenharmony_ci srq->db_index = mthca_alloc_db(dev, MTHCA_DB_TYPE_SRQ, 24862306a36Sopenharmony_ci srq->srqn, &srq->db); 24962306a36Sopenharmony_ci if (srq->db_index < 0) { 25062306a36Sopenharmony_ci err = -ENOMEM; 25162306a36Sopenharmony_ci goto err_out_icm; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 25762306a36Sopenharmony_ci if (IS_ERR(mailbox)) { 25862306a36Sopenharmony_ci err = PTR_ERR(mailbox); 25962306a36Sopenharmony_ci goto err_out_db; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci err = mthca_alloc_srq_buf(dev, pd, srq, udata); 26362306a36Sopenharmony_ci if (err) 26462306a36Sopenharmony_ci goto err_out_mailbox; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci spin_lock_init(&srq->lock); 26762306a36Sopenharmony_ci srq->refcount = 1; 26862306a36Sopenharmony_ci init_waitqueue_head(&srq->wait); 26962306a36Sopenharmony_ci mutex_init(&srq->mutex); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 27262306a36Sopenharmony_ci mthca_arbel_init_srq_context(dev, pd, srq, mailbox->buf, udata); 27362306a36Sopenharmony_ci else 27462306a36Sopenharmony_ci mthca_tavor_init_srq_context(dev, pd, srq, mailbox->buf, udata); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci err = mthca_SW2HW_SRQ(dev, mailbox, srq->srqn); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (err) { 27962306a36Sopenharmony_ci mthca_warn(dev, "SW2HW_SRQ failed (%d)\n", err); 28062306a36Sopenharmony_ci goto err_out_free_buf; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci spin_lock_irq(&dev->srq_table.lock); 28462306a36Sopenharmony_ci if (mthca_array_set(&dev->srq_table.srq, 28562306a36Sopenharmony_ci srq->srqn & (dev->limits.num_srqs - 1), 28662306a36Sopenharmony_ci srq)) { 28762306a36Sopenharmony_ci spin_unlock_irq(&dev->srq_table.lock); 28862306a36Sopenharmony_ci goto err_out_free_srq; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci spin_unlock_irq(&dev->srq_table.lock); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci srq->first_free = 0; 29562306a36Sopenharmony_ci srq->last_free = srq->max - 1; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci attr->max_wr = srq->max - 1; 29862306a36Sopenharmony_ci attr->max_sge = srq->max_gs; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cierr_out_free_srq: 30362306a36Sopenharmony_ci err = mthca_HW2SW_SRQ(dev, mailbox, srq->srqn); 30462306a36Sopenharmony_ci if (err) 30562306a36Sopenharmony_ci mthca_warn(dev, "HW2SW_SRQ failed (%d)\n", err); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cierr_out_free_buf: 30862306a36Sopenharmony_ci if (!udata) 30962306a36Sopenharmony_ci mthca_free_srq_buf(dev, srq); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cierr_out_mailbox: 31262306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cierr_out_db: 31562306a36Sopenharmony_ci if (!udata && mthca_is_memfree(dev)) 31662306a36Sopenharmony_ci mthca_free_db(dev, MTHCA_DB_TYPE_SRQ, srq->db_index); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cierr_out_icm: 31962306a36Sopenharmony_ci mthca_table_put(dev, dev->srq_table.table, srq->srqn); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cierr_out: 32262306a36Sopenharmony_ci mthca_free(&dev->srq_table.alloc, srq->srqn); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return err; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic inline int get_srq_refcount(struct mthca_dev *dev, struct mthca_srq *srq) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int c; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci spin_lock_irq(&dev->srq_table.lock); 33262306a36Sopenharmony_ci c = srq->refcount; 33362306a36Sopenharmony_ci spin_unlock_irq(&dev->srq_table.lock); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return c; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_civoid mthca_free_srq(struct mthca_dev *dev, struct mthca_srq *srq) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct mthca_mailbox *mailbox; 34162306a36Sopenharmony_ci int err; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 34462306a36Sopenharmony_ci if (IS_ERR(mailbox)) { 34562306a36Sopenharmony_ci mthca_warn(dev, "No memory for mailbox to free SRQ.\n"); 34662306a36Sopenharmony_ci return; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci err = mthca_HW2SW_SRQ(dev, mailbox, srq->srqn); 35062306a36Sopenharmony_ci if (err) 35162306a36Sopenharmony_ci mthca_warn(dev, "HW2SW_SRQ failed (%d)\n", err); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci spin_lock_irq(&dev->srq_table.lock); 35462306a36Sopenharmony_ci mthca_array_clear(&dev->srq_table.srq, 35562306a36Sopenharmony_ci srq->srqn & (dev->limits.num_srqs - 1)); 35662306a36Sopenharmony_ci --srq->refcount; 35762306a36Sopenharmony_ci spin_unlock_irq(&dev->srq_table.lock); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci wait_event(srq->wait, !get_srq_refcount(dev, srq)); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (!srq->ibsrq.uobject) { 36262306a36Sopenharmony_ci mthca_free_srq_buf(dev, srq); 36362306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 36462306a36Sopenharmony_ci mthca_free_db(dev, MTHCA_DB_TYPE_SRQ, srq->db_index); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci mthca_table_put(dev, dev->srq_table.table, srq->srqn); 36862306a36Sopenharmony_ci mthca_free(&dev->srq_table.alloc, srq->srqn); 36962306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ciint mthca_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, 37362306a36Sopenharmony_ci enum ib_srq_attr_mask attr_mask, struct ib_udata *udata) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct mthca_dev *dev = to_mdev(ibsrq->device); 37662306a36Sopenharmony_ci struct mthca_srq *srq = to_msrq(ibsrq); 37762306a36Sopenharmony_ci int ret = 0; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* We don't support resizing SRQs (yet?) */ 38062306a36Sopenharmony_ci if (attr_mask & IB_SRQ_MAX_WR) 38162306a36Sopenharmony_ci return -EINVAL; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (attr_mask & IB_SRQ_LIMIT) { 38462306a36Sopenharmony_ci u32 max_wr = mthca_is_memfree(dev) ? srq->max - 1 : srq->max; 38562306a36Sopenharmony_ci if (attr->srq_limit > max_wr) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci mutex_lock(&srq->mutex); 38962306a36Sopenharmony_ci ret = mthca_ARM_SRQ(dev, srq->srqn, attr->srq_limit); 39062306a36Sopenharmony_ci mutex_unlock(&srq->mutex); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ciint mthca_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct mthca_dev *dev = to_mdev(ibsrq->device); 39962306a36Sopenharmony_ci struct mthca_srq *srq = to_msrq(ibsrq); 40062306a36Sopenharmony_ci struct mthca_mailbox *mailbox; 40162306a36Sopenharmony_ci struct mthca_arbel_srq_context *arbel_ctx; 40262306a36Sopenharmony_ci struct mthca_tavor_srq_context *tavor_ctx; 40362306a36Sopenharmony_ci int err; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 40662306a36Sopenharmony_ci if (IS_ERR(mailbox)) 40762306a36Sopenharmony_ci return PTR_ERR(mailbox); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci err = mthca_QUERY_SRQ(dev, srq->srqn, mailbox); 41062306a36Sopenharmony_ci if (err) 41162306a36Sopenharmony_ci goto out; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (mthca_is_memfree(dev)) { 41462306a36Sopenharmony_ci arbel_ctx = mailbox->buf; 41562306a36Sopenharmony_ci srq_attr->srq_limit = be16_to_cpu(arbel_ctx->limit_watermark); 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci tavor_ctx = mailbox->buf; 41862306a36Sopenharmony_ci srq_attr->srq_limit = be16_to_cpu(tavor_ctx->limit_watermark); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci srq_attr->max_wr = srq->max - 1; 42262306a36Sopenharmony_ci srq_attr->max_sge = srq->max_gs; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciout: 42562306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return err; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_civoid mthca_srq_event(struct mthca_dev *dev, u32 srqn, 43162306a36Sopenharmony_ci enum ib_event_type event_type) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct mthca_srq *srq; 43462306a36Sopenharmony_ci struct ib_event event; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci spin_lock(&dev->srq_table.lock); 43762306a36Sopenharmony_ci srq = mthca_array_get(&dev->srq_table.srq, srqn & (dev->limits.num_srqs - 1)); 43862306a36Sopenharmony_ci if (srq) 43962306a36Sopenharmony_ci ++srq->refcount; 44062306a36Sopenharmony_ci spin_unlock(&dev->srq_table.lock); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (!srq) { 44362306a36Sopenharmony_ci mthca_warn(dev, "Async event for bogus SRQ %08x\n", srqn); 44462306a36Sopenharmony_ci return; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (!srq->ibsrq.event_handler) 44862306a36Sopenharmony_ci goto out; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci event.device = &dev->ib_dev; 45162306a36Sopenharmony_ci event.event = event_type; 45262306a36Sopenharmony_ci event.element.srq = &srq->ibsrq; 45362306a36Sopenharmony_ci srq->ibsrq.event_handler(&event, srq->ibsrq.srq_context); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciout: 45662306a36Sopenharmony_ci spin_lock(&dev->srq_table.lock); 45762306a36Sopenharmony_ci if (!--srq->refcount) 45862306a36Sopenharmony_ci wake_up(&srq->wait); 45962306a36Sopenharmony_ci spin_unlock(&dev->srq_table.lock); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/* 46362306a36Sopenharmony_ci * This function must be called with IRQs disabled. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_civoid mthca_free_srq_wqe(struct mthca_srq *srq, u32 wqe_addr) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci int ind; 46862306a36Sopenharmony_ci struct mthca_next_seg *last_free; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci ind = wqe_addr >> srq->wqe_shift; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci spin_lock(&srq->lock); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci last_free = get_wqe(srq, srq->last_free); 47562306a36Sopenharmony_ci *wqe_to_link(last_free) = ind; 47662306a36Sopenharmony_ci last_free->nda_op = htonl((ind << srq->wqe_shift) | 1); 47762306a36Sopenharmony_ci *wqe_to_link(get_wqe(srq, ind)) = -1; 47862306a36Sopenharmony_ci srq->last_free = ind; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci spin_unlock(&srq->lock); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciint mthca_tavor_post_srq_recv(struct ib_srq *ibsrq, const struct ib_recv_wr *wr, 48462306a36Sopenharmony_ci const struct ib_recv_wr **bad_wr) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct mthca_dev *dev = to_mdev(ibsrq->device); 48762306a36Sopenharmony_ci struct mthca_srq *srq = to_msrq(ibsrq); 48862306a36Sopenharmony_ci unsigned long flags; 48962306a36Sopenharmony_ci int err = 0; 49062306a36Sopenharmony_ci int first_ind; 49162306a36Sopenharmony_ci int ind; 49262306a36Sopenharmony_ci int next_ind; 49362306a36Sopenharmony_ci int nreq; 49462306a36Sopenharmony_ci int i; 49562306a36Sopenharmony_ci void *wqe; 49662306a36Sopenharmony_ci void *prev_wqe; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci spin_lock_irqsave(&srq->lock, flags); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci first_ind = srq->first_free; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci for (nreq = 0; wr; wr = wr->next) { 50362306a36Sopenharmony_ci ind = srq->first_free; 50462306a36Sopenharmony_ci wqe = get_wqe(srq, ind); 50562306a36Sopenharmony_ci next_ind = *wqe_to_link(wqe); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (unlikely(next_ind < 0)) { 50862306a36Sopenharmony_ci mthca_err(dev, "SRQ %06x full\n", srq->srqn); 50962306a36Sopenharmony_ci err = -ENOMEM; 51062306a36Sopenharmony_ci *bad_wr = wr; 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci prev_wqe = srq->last; 51562306a36Sopenharmony_ci srq->last = wqe; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ((struct mthca_next_seg *) wqe)->ee_nds = 0; 51862306a36Sopenharmony_ci /* flags field will always remain 0 */ 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci wqe += sizeof (struct mthca_next_seg); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (unlikely(wr->num_sge > srq->max_gs)) { 52362306a36Sopenharmony_ci err = -EINVAL; 52462306a36Sopenharmony_ci *bad_wr = wr; 52562306a36Sopenharmony_ci srq->last = prev_wqe; 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci for (i = 0; i < wr->num_sge; ++i) { 53062306a36Sopenharmony_ci mthca_set_data_seg(wqe, wr->sg_list + i); 53162306a36Sopenharmony_ci wqe += sizeof (struct mthca_data_seg); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (i < srq->max_gs) 53562306a36Sopenharmony_ci mthca_set_data_seg_inval(wqe); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci ((struct mthca_next_seg *) prev_wqe)->ee_nds = 53862306a36Sopenharmony_ci cpu_to_be32(MTHCA_NEXT_DBD); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci srq->wrid[ind] = wr->wr_id; 54162306a36Sopenharmony_ci srq->first_free = next_ind; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci ++nreq; 54462306a36Sopenharmony_ci if (unlikely(nreq == MTHCA_TAVOR_MAX_WQES_PER_RECV_DB)) { 54562306a36Sopenharmony_ci nreq = 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * Make sure that descriptors are written 54962306a36Sopenharmony_ci * before doorbell is rung. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci wmb(); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci mthca_write64(first_ind << srq->wqe_shift, srq->srqn << 8, 55462306a36Sopenharmony_ci dev->kar + MTHCA_RECEIVE_DOORBELL, 55562306a36Sopenharmony_ci MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock)); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci first_ind = srq->first_free; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (likely(nreq)) { 56262306a36Sopenharmony_ci /* 56362306a36Sopenharmony_ci * Make sure that descriptors are written before 56462306a36Sopenharmony_ci * doorbell is rung. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci wmb(); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci mthca_write64(first_ind << srq->wqe_shift, (srq->srqn << 8) | nreq, 56962306a36Sopenharmony_ci dev->kar + MTHCA_RECEIVE_DOORBELL, 57062306a36Sopenharmony_ci MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock)); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci spin_unlock_irqrestore(&srq->lock, flags); 57462306a36Sopenharmony_ci return err; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ciint mthca_arbel_post_srq_recv(struct ib_srq *ibsrq, const struct ib_recv_wr *wr, 57862306a36Sopenharmony_ci const struct ib_recv_wr **bad_wr) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct mthca_dev *dev = to_mdev(ibsrq->device); 58162306a36Sopenharmony_ci struct mthca_srq *srq = to_msrq(ibsrq); 58262306a36Sopenharmony_ci unsigned long flags; 58362306a36Sopenharmony_ci int err = 0; 58462306a36Sopenharmony_ci int ind; 58562306a36Sopenharmony_ci int next_ind; 58662306a36Sopenharmony_ci int nreq; 58762306a36Sopenharmony_ci int i; 58862306a36Sopenharmony_ci void *wqe; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci spin_lock_irqsave(&srq->lock, flags); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci for (nreq = 0; wr; ++nreq, wr = wr->next) { 59362306a36Sopenharmony_ci ind = srq->first_free; 59462306a36Sopenharmony_ci wqe = get_wqe(srq, ind); 59562306a36Sopenharmony_ci next_ind = *wqe_to_link(wqe); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (unlikely(next_ind < 0)) { 59862306a36Sopenharmony_ci mthca_err(dev, "SRQ %06x full\n", srq->srqn); 59962306a36Sopenharmony_ci err = -ENOMEM; 60062306a36Sopenharmony_ci *bad_wr = wr; 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ((struct mthca_next_seg *) wqe)->ee_nds = 0; 60562306a36Sopenharmony_ci /* flags field will always remain 0 */ 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci wqe += sizeof (struct mthca_next_seg); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (unlikely(wr->num_sge > srq->max_gs)) { 61062306a36Sopenharmony_ci err = -EINVAL; 61162306a36Sopenharmony_ci *bad_wr = wr; 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci for (i = 0; i < wr->num_sge; ++i) { 61662306a36Sopenharmony_ci mthca_set_data_seg(wqe, wr->sg_list + i); 61762306a36Sopenharmony_ci wqe += sizeof (struct mthca_data_seg); 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (i < srq->max_gs) 62162306a36Sopenharmony_ci mthca_set_data_seg_inval(wqe); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci srq->wrid[ind] = wr->wr_id; 62462306a36Sopenharmony_ci srq->first_free = next_ind; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (likely(nreq)) { 62862306a36Sopenharmony_ci srq->counter += nreq; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* 63162306a36Sopenharmony_ci * Make sure that descriptors are written before 63262306a36Sopenharmony_ci * we write doorbell record. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci wmb(); 63562306a36Sopenharmony_ci *srq->db = cpu_to_be32(srq->counter); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci spin_unlock_irqrestore(&srq->lock, flags); 63962306a36Sopenharmony_ci return err; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ciint mthca_max_srq_sge(struct mthca_dev *dev) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 64562306a36Sopenharmony_ci return dev->limits.max_sg; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * SRQ allocations are based on powers of 2 for Tavor, 64962306a36Sopenharmony_ci * (although they only need to be multiples of 16 bytes). 65062306a36Sopenharmony_ci * 65162306a36Sopenharmony_ci * Therefore, we need to base the max number of sg entries on 65262306a36Sopenharmony_ci * the largest power of 2 descriptor size that is <= to the 65362306a36Sopenharmony_ci * actual max WQE descriptor size, rather than return the 65462306a36Sopenharmony_ci * max_sg value given by the firmware (which is based on WQE 65562306a36Sopenharmony_ci * sizes as multiples of 16, not powers of 2). 65662306a36Sopenharmony_ci * 65762306a36Sopenharmony_ci * If SRQ implementation is changed for Tavor to be based on 65862306a36Sopenharmony_ci * multiples of 16, the calculation below can be deleted and 65962306a36Sopenharmony_ci * the FW max_sg value returned. 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_ci return min_t(int, dev->limits.max_sg, 66262306a36Sopenharmony_ci ((1 << (fls(dev->limits.max_desc_sz) - 1)) - 66362306a36Sopenharmony_ci sizeof (struct mthca_next_seg)) / 66462306a36Sopenharmony_ci sizeof (struct mthca_data_seg)); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ciint mthca_init_srq_table(struct mthca_dev *dev) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci int err; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (!(dev->mthca_flags & MTHCA_FLAG_SRQ)) 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci spin_lock_init(&dev->srq_table.lock); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci err = mthca_alloc_init(&dev->srq_table.alloc, 67762306a36Sopenharmony_ci dev->limits.num_srqs, 67862306a36Sopenharmony_ci dev->limits.num_srqs - 1, 67962306a36Sopenharmony_ci dev->limits.reserved_srqs); 68062306a36Sopenharmony_ci if (err) 68162306a36Sopenharmony_ci return err; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci err = mthca_array_init(&dev->srq_table.srq, 68462306a36Sopenharmony_ci dev->limits.num_srqs); 68562306a36Sopenharmony_ci if (err) 68662306a36Sopenharmony_ci mthca_alloc_cleanup(&dev->srq_table.alloc); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return err; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_civoid mthca_cleanup_srq_table(struct mthca_dev *dev) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci if (!(dev->mthca_flags & MTHCA_FLAG_SRQ)) 69462306a36Sopenharmony_ci return; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci mthca_array_cleanup(&dev->srq_table.srq, dev->limits.num_srqs); 69762306a36Sopenharmony_ci mthca_alloc_cleanup(&dev->srq_table.alloc); 69862306a36Sopenharmony_ci} 699