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