18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2018 Intel Corporation.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license.  When using or
58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
108c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
118c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
148c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
168c2ecf20Sopenharmony_ci * General Public License for more details.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * BSD LICENSE
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
218c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
228c2ecf20Sopenharmony_ci * are met:
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *  - Redistributions of source code must retain the above copyright
258c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
268c2ecf20Sopenharmony_ci *  - Redistributions in binary form must reproduce the above copyright
278c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
288c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
298c2ecf20Sopenharmony_ci *    distribution.
308c2ecf20Sopenharmony_ci *  - Neither the name of Intel Corporation nor the names of its
318c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
328c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
358c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
368c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
378c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
388c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
398c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
408c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
418c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
428c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
438c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
448c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#include <linux/slab.h>
498c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
508c2ecf20Sopenharmony_ci#include "cq.h"
518c2ecf20Sopenharmony_ci#include "vt.h"
528c2ecf20Sopenharmony_ci#include "trace.h"
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic struct workqueue_struct *comp_vector_wq;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/**
578c2ecf20Sopenharmony_ci * rvt_cq_enter - add a new entry to the completion queue
588c2ecf20Sopenharmony_ci * @cq: completion queue
598c2ecf20Sopenharmony_ci * @entry: work completion entry to add
608c2ecf20Sopenharmony_ci * @solicited: true if @entry is solicited
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * This may be called with qp->s_lock held.
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * Return: return true on success, else return
658c2ecf20Sopenharmony_ci * false if cq is full.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cibool rvt_cq_enter(struct rvt_cq *cq, struct ib_wc *entry, bool solicited)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct ib_uverbs_wc *uqueue = NULL;
708c2ecf20Sopenharmony_ci	struct ib_wc *kqueue = NULL;
718c2ecf20Sopenharmony_ci	struct rvt_cq_wc *u_wc = NULL;
728c2ecf20Sopenharmony_ci	struct rvt_k_cq_wc *k_wc = NULL;
738c2ecf20Sopenharmony_ci	unsigned long flags;
748c2ecf20Sopenharmony_ci	u32 head;
758c2ecf20Sopenharmony_ci	u32 next;
768c2ecf20Sopenharmony_ci	u32 tail;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cq->lock, flags);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (cq->ip) {
818c2ecf20Sopenharmony_ci		u_wc = cq->queue;
828c2ecf20Sopenharmony_ci		uqueue = &u_wc->uqueue[0];
838c2ecf20Sopenharmony_ci		head = RDMA_READ_UAPI_ATOMIC(u_wc->head);
848c2ecf20Sopenharmony_ci		tail = RDMA_READ_UAPI_ATOMIC(u_wc->tail);
858c2ecf20Sopenharmony_ci	} else {
868c2ecf20Sopenharmony_ci		k_wc = cq->kqueue;
878c2ecf20Sopenharmony_ci		kqueue = &k_wc->kqueue[0];
888c2ecf20Sopenharmony_ci		head = k_wc->head;
898c2ecf20Sopenharmony_ci		tail = k_wc->tail;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/*
938c2ecf20Sopenharmony_ci	 * Note that the head pointer might be writable by
948c2ecf20Sopenharmony_ci	 * user processes.Take care to verify it is a sane value.
958c2ecf20Sopenharmony_ci	 */
968c2ecf20Sopenharmony_ci	if (head >= (unsigned)cq->ibcq.cqe) {
978c2ecf20Sopenharmony_ci		head = cq->ibcq.cqe;
988c2ecf20Sopenharmony_ci		next = 0;
998c2ecf20Sopenharmony_ci	} else {
1008c2ecf20Sopenharmony_ci		next = head + 1;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (unlikely(next == tail || cq->cq_full)) {
1048c2ecf20Sopenharmony_ci		struct rvt_dev_info *rdi = cq->rdi;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		if (!cq->cq_full)
1078c2ecf20Sopenharmony_ci			rvt_pr_err_ratelimited(rdi, "CQ is full!\n");
1088c2ecf20Sopenharmony_ci		cq->cq_full = true;
1098c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&cq->lock, flags);
1108c2ecf20Sopenharmony_ci		if (cq->ibcq.event_handler) {
1118c2ecf20Sopenharmony_ci			struct ib_event ev;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci			ev.device = cq->ibcq.device;
1148c2ecf20Sopenharmony_ci			ev.element.cq = &cq->ibcq;
1158c2ecf20Sopenharmony_ci			ev.event = IB_EVENT_CQ_ERR;
1168c2ecf20Sopenharmony_ci			cq->ibcq.event_handler(&ev, cq->ibcq.cq_context);
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci		return false;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci	trace_rvt_cq_enter(cq, entry, head);
1218c2ecf20Sopenharmony_ci	if (uqueue) {
1228c2ecf20Sopenharmony_ci		uqueue[head].wr_id = entry->wr_id;
1238c2ecf20Sopenharmony_ci		uqueue[head].status = entry->status;
1248c2ecf20Sopenharmony_ci		uqueue[head].opcode = entry->opcode;
1258c2ecf20Sopenharmony_ci		uqueue[head].vendor_err = entry->vendor_err;
1268c2ecf20Sopenharmony_ci		uqueue[head].byte_len = entry->byte_len;
1278c2ecf20Sopenharmony_ci		uqueue[head].ex.imm_data = entry->ex.imm_data;
1288c2ecf20Sopenharmony_ci		uqueue[head].qp_num = entry->qp->qp_num;
1298c2ecf20Sopenharmony_ci		uqueue[head].src_qp = entry->src_qp;
1308c2ecf20Sopenharmony_ci		uqueue[head].wc_flags = entry->wc_flags;
1318c2ecf20Sopenharmony_ci		uqueue[head].pkey_index = entry->pkey_index;
1328c2ecf20Sopenharmony_ci		uqueue[head].slid = ib_lid_cpu16(entry->slid);
1338c2ecf20Sopenharmony_ci		uqueue[head].sl = entry->sl;
1348c2ecf20Sopenharmony_ci		uqueue[head].dlid_path_bits = entry->dlid_path_bits;
1358c2ecf20Sopenharmony_ci		uqueue[head].port_num = entry->port_num;
1368c2ecf20Sopenharmony_ci		/* Make sure entry is written before the head index. */
1378c2ecf20Sopenharmony_ci		RDMA_WRITE_UAPI_ATOMIC(u_wc->head, next);
1388c2ecf20Sopenharmony_ci	} else {
1398c2ecf20Sopenharmony_ci		kqueue[head] = *entry;
1408c2ecf20Sopenharmony_ci		k_wc->head = next;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (cq->notify == IB_CQ_NEXT_COMP ||
1448c2ecf20Sopenharmony_ci	    (cq->notify == IB_CQ_SOLICITED &&
1458c2ecf20Sopenharmony_ci	     (solicited || entry->status != IB_WC_SUCCESS))) {
1468c2ecf20Sopenharmony_ci		/*
1478c2ecf20Sopenharmony_ci		 * This will cause send_complete() to be called in
1488c2ecf20Sopenharmony_ci		 * another thread.
1498c2ecf20Sopenharmony_ci		 */
1508c2ecf20Sopenharmony_ci		cq->notify = RVT_CQ_NONE;
1518c2ecf20Sopenharmony_ci		cq->triggered++;
1528c2ecf20Sopenharmony_ci		queue_work_on(cq->comp_vector_cpu, comp_vector_wq,
1538c2ecf20Sopenharmony_ci			      &cq->comptask);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cq->lock, flags);
1578c2ecf20Sopenharmony_ci	return true;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rvt_cq_enter);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void send_complete(struct work_struct *work)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct rvt_cq *cq = container_of(work, struct rvt_cq, comptask);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/*
1668c2ecf20Sopenharmony_ci	 * The completion handler will most likely rearm the notification
1678c2ecf20Sopenharmony_ci	 * and poll for all pending entries.  If a new completion entry
1688c2ecf20Sopenharmony_ci	 * is added while we are in this routine, queue_work()
1698c2ecf20Sopenharmony_ci	 * won't call us again until we return so we check triggered to
1708c2ecf20Sopenharmony_ci	 * see if we need to call the handler again.
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	for (;;) {
1738c2ecf20Sopenharmony_ci		u8 triggered = cq->triggered;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		/*
1768c2ecf20Sopenharmony_ci		 * IPoIB connected mode assumes the callback is from a
1778c2ecf20Sopenharmony_ci		 * soft IRQ. We simulate this by blocking "bottom halves".
1788c2ecf20Sopenharmony_ci		 * See the implementation for ipoib_cm_handle_tx_wc(),
1798c2ecf20Sopenharmony_ci		 * netif_tx_lock_bh() and netif_tx_lock().
1808c2ecf20Sopenharmony_ci		 */
1818c2ecf20Sopenharmony_ci		local_bh_disable();
1828c2ecf20Sopenharmony_ci		cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
1838c2ecf20Sopenharmony_ci		local_bh_enable();
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		if (cq->triggered == triggered)
1868c2ecf20Sopenharmony_ci			return;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/**
1918c2ecf20Sopenharmony_ci * rvt_create_cq - create a completion queue
1928c2ecf20Sopenharmony_ci * @ibcq: Allocated CQ
1938c2ecf20Sopenharmony_ci * @attr: creation attributes
1948c2ecf20Sopenharmony_ci * @udata: user data for libibverbs.so
1958c2ecf20Sopenharmony_ci *
1968c2ecf20Sopenharmony_ci * Called by ib_create_cq() in the generic verbs code.
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci * Return: 0 on success
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_ciint rvt_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
2018c2ecf20Sopenharmony_ci		  struct ib_udata *udata)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct ib_device *ibdev = ibcq->device;
2048c2ecf20Sopenharmony_ci	struct rvt_dev_info *rdi = ib_to_rvt(ibdev);
2058c2ecf20Sopenharmony_ci	struct rvt_cq *cq = ibcq_to_rvtcq(ibcq);
2068c2ecf20Sopenharmony_ci	struct rvt_cq_wc *u_wc = NULL;
2078c2ecf20Sopenharmony_ci	struct rvt_k_cq_wc *k_wc = NULL;
2088c2ecf20Sopenharmony_ci	u32 sz;
2098c2ecf20Sopenharmony_ci	unsigned int entries = attr->cqe;
2108c2ecf20Sopenharmony_ci	int comp_vector = attr->comp_vector;
2118c2ecf20Sopenharmony_ci	int err;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (attr->flags)
2148c2ecf20Sopenharmony_ci		return -EINVAL;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (entries < 1 || entries > rdi->dparms.props.max_cqe)
2178c2ecf20Sopenharmony_ci		return -EINVAL;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (comp_vector < 0)
2208c2ecf20Sopenharmony_ci		comp_vector = 0;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	comp_vector = comp_vector % rdi->ibdev.num_comp_vectors;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/*
2258c2ecf20Sopenharmony_ci	 * Allocate the completion queue entries and head/tail pointers.
2268c2ecf20Sopenharmony_ci	 * This is allocated separately so that it can be resized and
2278c2ecf20Sopenharmony_ci	 * also mapped into user space.
2288c2ecf20Sopenharmony_ci	 * We need to use vmalloc() in order to support mmap and large
2298c2ecf20Sopenharmony_ci	 * numbers of entries.
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	if (udata && udata->outlen >= sizeof(__u64)) {
2328c2ecf20Sopenharmony_ci		sz = sizeof(struct ib_uverbs_wc) * (entries + 1);
2338c2ecf20Sopenharmony_ci		sz += sizeof(*u_wc);
2348c2ecf20Sopenharmony_ci		u_wc = vmalloc_user(sz);
2358c2ecf20Sopenharmony_ci		if (!u_wc)
2368c2ecf20Sopenharmony_ci			return -ENOMEM;
2378c2ecf20Sopenharmony_ci	} else {
2388c2ecf20Sopenharmony_ci		sz = sizeof(struct ib_wc) * (entries + 1);
2398c2ecf20Sopenharmony_ci		sz += sizeof(*k_wc);
2408c2ecf20Sopenharmony_ci		k_wc = vzalloc_node(sz, rdi->dparms.node);
2418c2ecf20Sopenharmony_ci		if (!k_wc)
2428c2ecf20Sopenharmony_ci			return -ENOMEM;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/*
2468c2ecf20Sopenharmony_ci	 * Return the address of the WC as the offset to mmap.
2478c2ecf20Sopenharmony_ci	 * See rvt_mmap() for details.
2488c2ecf20Sopenharmony_ci	 */
2498c2ecf20Sopenharmony_ci	if (udata && udata->outlen >= sizeof(__u64)) {
2508c2ecf20Sopenharmony_ci		cq->ip = rvt_create_mmap_info(rdi, sz, udata, u_wc);
2518c2ecf20Sopenharmony_ci		if (IS_ERR(cq->ip)) {
2528c2ecf20Sopenharmony_ci			err = PTR_ERR(cq->ip);
2538c2ecf20Sopenharmony_ci			goto bail_wc;
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		err = ib_copy_to_udata(udata, &cq->ip->offset,
2578c2ecf20Sopenharmony_ci				       sizeof(cq->ip->offset));
2588c2ecf20Sopenharmony_ci		if (err)
2598c2ecf20Sopenharmony_ci			goto bail_ip;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	spin_lock_irq(&rdi->n_cqs_lock);
2638c2ecf20Sopenharmony_ci	if (rdi->n_cqs_allocated == rdi->dparms.props.max_cq) {
2648c2ecf20Sopenharmony_ci		spin_unlock_irq(&rdi->n_cqs_lock);
2658c2ecf20Sopenharmony_ci		err = -ENOMEM;
2668c2ecf20Sopenharmony_ci		goto bail_ip;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	rdi->n_cqs_allocated++;
2708c2ecf20Sopenharmony_ci	spin_unlock_irq(&rdi->n_cqs_lock);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (cq->ip) {
2738c2ecf20Sopenharmony_ci		spin_lock_irq(&rdi->pending_lock);
2748c2ecf20Sopenharmony_ci		list_add(&cq->ip->pending_mmaps, &rdi->pending_mmaps);
2758c2ecf20Sopenharmony_ci		spin_unlock_irq(&rdi->pending_lock);
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/*
2798c2ecf20Sopenharmony_ci	 * ib_create_cq() will initialize cq->ibcq except for cq->ibcq.cqe.
2808c2ecf20Sopenharmony_ci	 * The number of entries should be >= the number requested or return
2818c2ecf20Sopenharmony_ci	 * an error.
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	cq->rdi = rdi;
2848c2ecf20Sopenharmony_ci	if (rdi->driver_f.comp_vect_cpu_lookup)
2858c2ecf20Sopenharmony_ci		cq->comp_vector_cpu =
2868c2ecf20Sopenharmony_ci			rdi->driver_f.comp_vect_cpu_lookup(rdi, comp_vector);
2878c2ecf20Sopenharmony_ci	else
2888c2ecf20Sopenharmony_ci		cq->comp_vector_cpu =
2898c2ecf20Sopenharmony_ci			cpumask_first(cpumask_of_node(rdi->dparms.node));
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	cq->ibcq.cqe = entries;
2928c2ecf20Sopenharmony_ci	cq->notify = RVT_CQ_NONE;
2938c2ecf20Sopenharmony_ci	spin_lock_init(&cq->lock);
2948c2ecf20Sopenharmony_ci	INIT_WORK(&cq->comptask, send_complete);
2958c2ecf20Sopenharmony_ci	if (u_wc)
2968c2ecf20Sopenharmony_ci		cq->queue = u_wc;
2978c2ecf20Sopenharmony_ci	else
2988c2ecf20Sopenharmony_ci		cq->kqueue = k_wc;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	trace_rvt_create_cq(cq, attr);
3018c2ecf20Sopenharmony_ci	return 0;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cibail_ip:
3048c2ecf20Sopenharmony_ci	kfree(cq->ip);
3058c2ecf20Sopenharmony_cibail_wc:
3068c2ecf20Sopenharmony_ci	vfree(u_wc);
3078c2ecf20Sopenharmony_ci	vfree(k_wc);
3088c2ecf20Sopenharmony_ci	return err;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci/**
3128c2ecf20Sopenharmony_ci * rvt_destroy_cq - destroy a completion queue
3138c2ecf20Sopenharmony_ci * @ibcq: the completion queue to destroy.
3148c2ecf20Sopenharmony_ci * @udata: user data or NULL for kernel object
3158c2ecf20Sopenharmony_ci *
3168c2ecf20Sopenharmony_ci * Called by ib_destroy_cq() in the generic verbs code.
3178c2ecf20Sopenharmony_ci */
3188c2ecf20Sopenharmony_ciint rvt_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct rvt_cq *cq = ibcq_to_rvtcq(ibcq);
3218c2ecf20Sopenharmony_ci	struct rvt_dev_info *rdi = cq->rdi;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	flush_work(&cq->comptask);
3248c2ecf20Sopenharmony_ci	spin_lock_irq(&rdi->n_cqs_lock);
3258c2ecf20Sopenharmony_ci	rdi->n_cqs_allocated--;
3268c2ecf20Sopenharmony_ci	spin_unlock_irq(&rdi->n_cqs_lock);
3278c2ecf20Sopenharmony_ci	if (cq->ip)
3288c2ecf20Sopenharmony_ci		kref_put(&cq->ip->ref, rvt_release_mmap_info);
3298c2ecf20Sopenharmony_ci	else
3308c2ecf20Sopenharmony_ci		vfree(cq->kqueue);
3318c2ecf20Sopenharmony_ci	return 0;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/**
3358c2ecf20Sopenharmony_ci * rvt_req_notify_cq - change the notification type for a completion queue
3368c2ecf20Sopenharmony_ci * @ibcq: the completion queue
3378c2ecf20Sopenharmony_ci * @notify_flags: the type of notification to request
3388c2ecf20Sopenharmony_ci *
3398c2ecf20Sopenharmony_ci * This may be called from interrupt context.  Also called by
3408c2ecf20Sopenharmony_ci * ib_req_notify_cq() in the generic verbs code.
3418c2ecf20Sopenharmony_ci *
3428c2ecf20Sopenharmony_ci * Return: 0 for success.
3438c2ecf20Sopenharmony_ci */
3448c2ecf20Sopenharmony_ciint rvt_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_flags)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct rvt_cq *cq = ibcq_to_rvtcq(ibcq);
3478c2ecf20Sopenharmony_ci	unsigned long flags;
3488c2ecf20Sopenharmony_ci	int ret = 0;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cq->lock, flags);
3518c2ecf20Sopenharmony_ci	/*
3528c2ecf20Sopenharmony_ci	 * Don't change IB_CQ_NEXT_COMP to IB_CQ_SOLICITED but allow
3538c2ecf20Sopenharmony_ci	 * any other transitions (see C11-31 and C11-32 in ch. 11.4.2.2).
3548c2ecf20Sopenharmony_ci	 */
3558c2ecf20Sopenharmony_ci	if (cq->notify != IB_CQ_NEXT_COMP)
3568c2ecf20Sopenharmony_ci		cq->notify = notify_flags & IB_CQ_SOLICITED_MASK;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (notify_flags & IB_CQ_REPORT_MISSED_EVENTS) {
3598c2ecf20Sopenharmony_ci		if (cq->queue) {
3608c2ecf20Sopenharmony_ci			if (RDMA_READ_UAPI_ATOMIC(cq->queue->head) !=
3618c2ecf20Sopenharmony_ci				RDMA_READ_UAPI_ATOMIC(cq->queue->tail))
3628c2ecf20Sopenharmony_ci				ret = 1;
3638c2ecf20Sopenharmony_ci		} else {
3648c2ecf20Sopenharmony_ci			if (cq->kqueue->head != cq->kqueue->tail)
3658c2ecf20Sopenharmony_ci				ret = 1;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cq->lock, flags);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return ret;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci/**
3758c2ecf20Sopenharmony_ci * rvt_resize_cq - change the size of the CQ
3768c2ecf20Sopenharmony_ci * @ibcq: the completion queue
3778c2ecf20Sopenharmony_ci *
3788c2ecf20Sopenharmony_ci * Return: 0 for success.
3798c2ecf20Sopenharmony_ci */
3808c2ecf20Sopenharmony_ciint rvt_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct rvt_cq *cq = ibcq_to_rvtcq(ibcq);
3838c2ecf20Sopenharmony_ci	u32 head, tail, n;
3848c2ecf20Sopenharmony_ci	int ret;
3858c2ecf20Sopenharmony_ci	u32 sz;
3868c2ecf20Sopenharmony_ci	struct rvt_dev_info *rdi = cq->rdi;
3878c2ecf20Sopenharmony_ci	struct rvt_cq_wc *u_wc = NULL;
3888c2ecf20Sopenharmony_ci	struct rvt_cq_wc *old_u_wc = NULL;
3898c2ecf20Sopenharmony_ci	struct rvt_k_cq_wc *k_wc = NULL;
3908c2ecf20Sopenharmony_ci	struct rvt_k_cq_wc *old_k_wc = NULL;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (cqe < 1 || cqe > rdi->dparms.props.max_cqe)
3938c2ecf20Sopenharmony_ci		return -EINVAL;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/*
3968c2ecf20Sopenharmony_ci	 * Need to use vmalloc() if we want to support large #s of entries.
3978c2ecf20Sopenharmony_ci	 */
3988c2ecf20Sopenharmony_ci	if (udata && udata->outlen >= sizeof(__u64)) {
3998c2ecf20Sopenharmony_ci		sz = sizeof(struct ib_uverbs_wc) * (cqe + 1);
4008c2ecf20Sopenharmony_ci		sz += sizeof(*u_wc);
4018c2ecf20Sopenharmony_ci		u_wc = vmalloc_user(sz);
4028c2ecf20Sopenharmony_ci		if (!u_wc)
4038c2ecf20Sopenharmony_ci			return -ENOMEM;
4048c2ecf20Sopenharmony_ci	} else {
4058c2ecf20Sopenharmony_ci		sz = sizeof(struct ib_wc) * (cqe + 1);
4068c2ecf20Sopenharmony_ci		sz += sizeof(*k_wc);
4078c2ecf20Sopenharmony_ci		k_wc = vzalloc_node(sz, rdi->dparms.node);
4088c2ecf20Sopenharmony_ci		if (!k_wc)
4098c2ecf20Sopenharmony_ci			return -ENOMEM;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	/* Check that we can write the offset to mmap. */
4128c2ecf20Sopenharmony_ci	if (udata && udata->outlen >= sizeof(__u64)) {
4138c2ecf20Sopenharmony_ci		__u64 offset = 0;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		ret = ib_copy_to_udata(udata, &offset, sizeof(offset));
4168c2ecf20Sopenharmony_ci		if (ret)
4178c2ecf20Sopenharmony_ci			goto bail_free;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	spin_lock_irq(&cq->lock);
4218c2ecf20Sopenharmony_ci	/*
4228c2ecf20Sopenharmony_ci	 * Make sure head and tail are sane since they
4238c2ecf20Sopenharmony_ci	 * might be user writable.
4248c2ecf20Sopenharmony_ci	 */
4258c2ecf20Sopenharmony_ci	if (u_wc) {
4268c2ecf20Sopenharmony_ci		old_u_wc = cq->queue;
4278c2ecf20Sopenharmony_ci		head = RDMA_READ_UAPI_ATOMIC(old_u_wc->head);
4288c2ecf20Sopenharmony_ci		tail = RDMA_READ_UAPI_ATOMIC(old_u_wc->tail);
4298c2ecf20Sopenharmony_ci	} else {
4308c2ecf20Sopenharmony_ci		old_k_wc = cq->kqueue;
4318c2ecf20Sopenharmony_ci		head = old_k_wc->head;
4328c2ecf20Sopenharmony_ci		tail = old_k_wc->tail;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (head > (u32)cq->ibcq.cqe)
4368c2ecf20Sopenharmony_ci		head = (u32)cq->ibcq.cqe;
4378c2ecf20Sopenharmony_ci	if (tail > (u32)cq->ibcq.cqe)
4388c2ecf20Sopenharmony_ci		tail = (u32)cq->ibcq.cqe;
4398c2ecf20Sopenharmony_ci	if (head < tail)
4408c2ecf20Sopenharmony_ci		n = cq->ibcq.cqe + 1 + head - tail;
4418c2ecf20Sopenharmony_ci	else
4428c2ecf20Sopenharmony_ci		n = head - tail;
4438c2ecf20Sopenharmony_ci	if (unlikely((u32)cqe < n)) {
4448c2ecf20Sopenharmony_ci		ret = -EINVAL;
4458c2ecf20Sopenharmony_ci		goto bail_unlock;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci	for (n = 0; tail != head; n++) {
4488c2ecf20Sopenharmony_ci		if (u_wc)
4498c2ecf20Sopenharmony_ci			u_wc->uqueue[n] = old_u_wc->uqueue[tail];
4508c2ecf20Sopenharmony_ci		else
4518c2ecf20Sopenharmony_ci			k_wc->kqueue[n] = old_k_wc->kqueue[tail];
4528c2ecf20Sopenharmony_ci		if (tail == (u32)cq->ibcq.cqe)
4538c2ecf20Sopenharmony_ci			tail = 0;
4548c2ecf20Sopenharmony_ci		else
4558c2ecf20Sopenharmony_ci			tail++;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	cq->ibcq.cqe = cqe;
4588c2ecf20Sopenharmony_ci	if (u_wc) {
4598c2ecf20Sopenharmony_ci		RDMA_WRITE_UAPI_ATOMIC(u_wc->head, n);
4608c2ecf20Sopenharmony_ci		RDMA_WRITE_UAPI_ATOMIC(u_wc->tail, 0);
4618c2ecf20Sopenharmony_ci		cq->queue = u_wc;
4628c2ecf20Sopenharmony_ci	} else {
4638c2ecf20Sopenharmony_ci		k_wc->head = n;
4648c2ecf20Sopenharmony_ci		k_wc->tail = 0;
4658c2ecf20Sopenharmony_ci		cq->kqueue = k_wc;
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci	spin_unlock_irq(&cq->lock);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (u_wc)
4708c2ecf20Sopenharmony_ci		vfree(old_u_wc);
4718c2ecf20Sopenharmony_ci	else
4728c2ecf20Sopenharmony_ci		vfree(old_k_wc);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (cq->ip) {
4758c2ecf20Sopenharmony_ci		struct rvt_mmap_info *ip = cq->ip;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci		rvt_update_mmap_info(rdi, ip, sz, u_wc);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		/*
4808c2ecf20Sopenharmony_ci		 * Return the offset to mmap.
4818c2ecf20Sopenharmony_ci		 * See rvt_mmap() for details.
4828c2ecf20Sopenharmony_ci		 */
4838c2ecf20Sopenharmony_ci		if (udata && udata->outlen >= sizeof(__u64)) {
4848c2ecf20Sopenharmony_ci			ret = ib_copy_to_udata(udata, &ip->offset,
4858c2ecf20Sopenharmony_ci					       sizeof(ip->offset));
4868c2ecf20Sopenharmony_ci			if (ret)
4878c2ecf20Sopenharmony_ci				return ret;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		spin_lock_irq(&rdi->pending_lock);
4918c2ecf20Sopenharmony_ci		if (list_empty(&ip->pending_mmaps))
4928c2ecf20Sopenharmony_ci			list_add(&ip->pending_mmaps, &rdi->pending_mmaps);
4938c2ecf20Sopenharmony_ci		spin_unlock_irq(&rdi->pending_lock);
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return 0;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cibail_unlock:
4998c2ecf20Sopenharmony_ci	spin_unlock_irq(&cq->lock);
5008c2ecf20Sopenharmony_cibail_free:
5018c2ecf20Sopenharmony_ci	vfree(u_wc);
5028c2ecf20Sopenharmony_ci	vfree(k_wc);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return ret;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/**
5088c2ecf20Sopenharmony_ci * rvt_poll_cq - poll for work completion entries
5098c2ecf20Sopenharmony_ci * @ibcq: the completion queue to poll
5108c2ecf20Sopenharmony_ci * @num_entries: the maximum number of entries to return
5118c2ecf20Sopenharmony_ci * @entry: pointer to array where work completions are placed
5128c2ecf20Sopenharmony_ci *
5138c2ecf20Sopenharmony_ci * This may be called from interrupt context.  Also called by ib_poll_cq()
5148c2ecf20Sopenharmony_ci * in the generic verbs code.
5158c2ecf20Sopenharmony_ci *
5168c2ecf20Sopenharmony_ci * Return: the number of completion entries polled.
5178c2ecf20Sopenharmony_ci */
5188c2ecf20Sopenharmony_ciint rvt_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	struct rvt_cq *cq = ibcq_to_rvtcq(ibcq);
5218c2ecf20Sopenharmony_ci	struct rvt_k_cq_wc *wc;
5228c2ecf20Sopenharmony_ci	unsigned long flags;
5238c2ecf20Sopenharmony_ci	int npolled;
5248c2ecf20Sopenharmony_ci	u32 tail;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* The kernel can only poll a kernel completion queue */
5278c2ecf20Sopenharmony_ci	if (cq->ip)
5288c2ecf20Sopenharmony_ci		return -EINVAL;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cq->lock, flags);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	wc = cq->kqueue;
5338c2ecf20Sopenharmony_ci	tail = wc->tail;
5348c2ecf20Sopenharmony_ci	if (tail > (u32)cq->ibcq.cqe)
5358c2ecf20Sopenharmony_ci		tail = (u32)cq->ibcq.cqe;
5368c2ecf20Sopenharmony_ci	for (npolled = 0; npolled < num_entries; ++npolled, ++entry) {
5378c2ecf20Sopenharmony_ci		if (tail == wc->head)
5388c2ecf20Sopenharmony_ci			break;
5398c2ecf20Sopenharmony_ci		/* The kernel doesn't need a RMB since it has the lock. */
5408c2ecf20Sopenharmony_ci		trace_rvt_cq_poll(cq, &wc->kqueue[tail], npolled);
5418c2ecf20Sopenharmony_ci		*entry = wc->kqueue[tail];
5428c2ecf20Sopenharmony_ci		if (tail >= cq->ibcq.cqe)
5438c2ecf20Sopenharmony_ci			tail = 0;
5448c2ecf20Sopenharmony_ci		else
5458c2ecf20Sopenharmony_ci			tail++;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	wc->tail = tail;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cq->lock, flags);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	return npolled;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/**
5558c2ecf20Sopenharmony_ci * rvt_driver_cq_init - Init cq resources on behalf of driver
5568c2ecf20Sopenharmony_ci *
5578c2ecf20Sopenharmony_ci * Return: 0 on success
5588c2ecf20Sopenharmony_ci */
5598c2ecf20Sopenharmony_ciint rvt_driver_cq_init(void)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	comp_vector_wq = alloc_workqueue("%s", WQ_HIGHPRI | WQ_CPU_INTENSIVE,
5628c2ecf20Sopenharmony_ci					 0, "rdmavt_cq");
5638c2ecf20Sopenharmony_ci	if (!comp_vector_wq)
5648c2ecf20Sopenharmony_ci		return -ENOMEM;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	return 0;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/**
5708c2ecf20Sopenharmony_ci * rvt_cq_exit - tear down cq reources
5718c2ecf20Sopenharmony_ci */
5728c2ecf20Sopenharmony_civoid rvt_cq_exit(void)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	destroy_workqueue(comp_vector_wq);
5758c2ecf20Sopenharmony_ci	comp_vector_wq = NULL;
5768c2ecf20Sopenharmony_ci}
577