18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
48c2ecf20Sopenharmony_ci * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
88c2ecf20Sopenharmony_ci#include "rxe.h"
98c2ecf20Sopenharmony_ci#include "rxe_loc.h"
108c2ecf20Sopenharmony_ci#include "rxe_queue.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ciint do_mmap_info(struct rxe_dev *rxe, struct mminfo __user *outbuf,
138c2ecf20Sopenharmony_ci		 struct ib_udata *udata, struct rxe_queue_buf *buf,
148c2ecf20Sopenharmony_ci		 size_t buf_size, struct rxe_mmap_info **ip_p)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	int err;
178c2ecf20Sopenharmony_ci	struct rxe_mmap_info *ip = NULL;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	if (outbuf) {
208c2ecf20Sopenharmony_ci		ip = rxe_create_mmap_info(rxe, buf_size, udata, buf);
218c2ecf20Sopenharmony_ci		if (IS_ERR(ip)) {
228c2ecf20Sopenharmony_ci			err = PTR_ERR(ip);
238c2ecf20Sopenharmony_ci			goto err1;
248c2ecf20Sopenharmony_ci		}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci		if (copy_to_user(outbuf, &ip->info, sizeof(ip->info))) {
278c2ecf20Sopenharmony_ci			err = -EFAULT;
288c2ecf20Sopenharmony_ci			goto err2;
298c2ecf20Sopenharmony_ci		}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci		spin_lock_bh(&rxe->pending_lock);
328c2ecf20Sopenharmony_ci		list_add(&ip->pending_mmaps, &rxe->pending_mmaps);
338c2ecf20Sopenharmony_ci		spin_unlock_bh(&rxe->pending_lock);
348c2ecf20Sopenharmony_ci	}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	*ip_p = ip;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return 0;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cierr2:
418c2ecf20Sopenharmony_ci	kfree(ip);
428c2ecf20Sopenharmony_cierr1:
438c2ecf20Sopenharmony_ci	return err;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ciinline void rxe_queue_reset(struct rxe_queue *q)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	/* queue is comprised from header and the memory
498c2ecf20Sopenharmony_ci	 * of the actual queue. See "struct rxe_queue_buf" in rxe_queue.h
508c2ecf20Sopenharmony_ci	 * reset only the queue itself and not the management header
518c2ecf20Sopenharmony_ci	 */
528c2ecf20Sopenharmony_ci	memset(q->buf->data, 0, q->buf_size - sizeof(struct rxe_queue_buf));
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct rxe_queue *rxe_queue_init(struct rxe_dev *rxe,
568c2ecf20Sopenharmony_ci				 int *num_elem,
578c2ecf20Sopenharmony_ci				 unsigned int elem_size)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct rxe_queue *q;
608c2ecf20Sopenharmony_ci	size_t buf_size;
618c2ecf20Sopenharmony_ci	unsigned int num_slots;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/* num_elem == 0 is allowed, but uninteresting */
648c2ecf20Sopenharmony_ci	if (*num_elem < 0)
658c2ecf20Sopenharmony_ci		goto err1;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	q = kmalloc(sizeof(*q), GFP_KERNEL);
688c2ecf20Sopenharmony_ci	if (!q)
698c2ecf20Sopenharmony_ci		goto err1;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	q->rxe = rxe;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* used in resize, only need to copy used part of queue */
748c2ecf20Sopenharmony_ci	q->elem_size = elem_size;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* pad element up to at least a cacheline and always a power of 2 */
778c2ecf20Sopenharmony_ci	if (elem_size < cache_line_size())
788c2ecf20Sopenharmony_ci		elem_size = cache_line_size();
798c2ecf20Sopenharmony_ci	elem_size = roundup_pow_of_two(elem_size);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	q->log2_elem_size = order_base_2(elem_size);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	num_slots = *num_elem + 1;
848c2ecf20Sopenharmony_ci	num_slots = roundup_pow_of_two(num_slots);
858c2ecf20Sopenharmony_ci	q->index_mask = num_slots - 1;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	buf_size = sizeof(struct rxe_queue_buf) + num_slots * elem_size;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	q->buf = vmalloc_user(buf_size);
908c2ecf20Sopenharmony_ci	if (!q->buf)
918c2ecf20Sopenharmony_ci		goto err2;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	q->buf->log2_elem_size = q->log2_elem_size;
948c2ecf20Sopenharmony_ci	q->buf->index_mask = q->index_mask;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	q->buf_size = buf_size;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	*num_elem = num_slots - 1;
998c2ecf20Sopenharmony_ci	return q;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cierr2:
1028c2ecf20Sopenharmony_ci	kfree(q);
1038c2ecf20Sopenharmony_cierr1:
1048c2ecf20Sopenharmony_ci	return NULL;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* copies elements from original q to new q and then swaps the contents of the
1088c2ecf20Sopenharmony_ci * two q headers. This is so that if anyone is holding a pointer to q it will
1098c2ecf20Sopenharmony_ci * still work
1108c2ecf20Sopenharmony_ci */
1118c2ecf20Sopenharmony_cistatic int resize_finish(struct rxe_queue *q, struct rxe_queue *new_q,
1128c2ecf20Sopenharmony_ci			 unsigned int num_elem)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	if (!queue_empty(q) && (num_elem < queue_count(q)))
1158c2ecf20Sopenharmony_ci		return -EINVAL;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	while (!queue_empty(q)) {
1188c2ecf20Sopenharmony_ci		memcpy(producer_addr(new_q), consumer_addr(q),
1198c2ecf20Sopenharmony_ci		       new_q->elem_size);
1208c2ecf20Sopenharmony_ci		advance_producer(new_q);
1218c2ecf20Sopenharmony_ci		advance_consumer(q);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	swap(*q, *new_q);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return 0;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciint rxe_queue_resize(struct rxe_queue *q, unsigned int *num_elem_p,
1308c2ecf20Sopenharmony_ci		     unsigned int elem_size, struct ib_udata *udata,
1318c2ecf20Sopenharmony_ci		     struct mminfo __user *outbuf, spinlock_t *producer_lock,
1328c2ecf20Sopenharmony_ci		     spinlock_t *consumer_lock)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct rxe_queue *new_q;
1358c2ecf20Sopenharmony_ci	unsigned int num_elem = *num_elem_p;
1368c2ecf20Sopenharmony_ci	int err;
1378c2ecf20Sopenharmony_ci	unsigned long flags = 0, flags1;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	new_q = rxe_queue_init(q->rxe, &num_elem, elem_size);
1408c2ecf20Sopenharmony_ci	if (!new_q)
1418c2ecf20Sopenharmony_ci		return -ENOMEM;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	err = do_mmap_info(new_q->rxe, outbuf, udata, new_q->buf,
1448c2ecf20Sopenharmony_ci			   new_q->buf_size, &new_q->ip);
1458c2ecf20Sopenharmony_ci	if (err) {
1468c2ecf20Sopenharmony_ci		vfree(new_q->buf);
1478c2ecf20Sopenharmony_ci		kfree(new_q);
1488c2ecf20Sopenharmony_ci		goto err1;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	spin_lock_irqsave(consumer_lock, flags1);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (producer_lock) {
1548c2ecf20Sopenharmony_ci		spin_lock_irqsave(producer_lock, flags);
1558c2ecf20Sopenharmony_ci		err = resize_finish(q, new_q, num_elem);
1568c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(producer_lock, flags);
1578c2ecf20Sopenharmony_ci	} else {
1588c2ecf20Sopenharmony_ci		err = resize_finish(q, new_q, num_elem);
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(consumer_lock, flags1);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	rxe_queue_cleanup(new_q);	/* new/old dep on err */
1648c2ecf20Sopenharmony_ci	if (err)
1658c2ecf20Sopenharmony_ci		goto err1;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	*num_elem_p = num_elem;
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cierr1:
1718c2ecf20Sopenharmony_ci	return err;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_civoid rxe_queue_cleanup(struct rxe_queue *q)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	if (q->ip)
1778c2ecf20Sopenharmony_ci		kref_put(&q->ip->ref, rxe_mmap_release);
1788c2ecf20Sopenharmony_ci	else
1798c2ecf20Sopenharmony_ci		vfree(q->buf);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	kfree(q);
1828c2ecf20Sopenharmony_ci}
183