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_loc.h" 1062306a36Sopenharmony_ci#include "rxe_queue.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciint do_mmap_info(struct rxe_dev *rxe, struct mminfo __user *outbuf, 1362306a36Sopenharmony_ci struct ib_udata *udata, struct rxe_queue_buf *buf, 1462306a36Sopenharmony_ci size_t buf_size, struct rxe_mmap_info **ip_p) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci int err; 1762306a36Sopenharmony_ci struct rxe_mmap_info *ip = NULL; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci if (outbuf) { 2062306a36Sopenharmony_ci ip = rxe_create_mmap_info(rxe, buf_size, udata, buf); 2162306a36Sopenharmony_ci if (IS_ERR(ip)) { 2262306a36Sopenharmony_ci err = PTR_ERR(ip); 2362306a36Sopenharmony_ci goto err1; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (copy_to_user(outbuf, &ip->info, sizeof(ip->info))) { 2762306a36Sopenharmony_ci err = -EFAULT; 2862306a36Sopenharmony_ci goto err2; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci spin_lock_bh(&rxe->pending_lock); 3262306a36Sopenharmony_ci list_add(&ip->pending_mmaps, &rxe->pending_mmaps); 3362306a36Sopenharmony_ci spin_unlock_bh(&rxe->pending_lock); 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci *ip_p = ip; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cierr2: 4162306a36Sopenharmony_ci kfree(ip); 4262306a36Sopenharmony_cierr1: 4362306a36Sopenharmony_ci return err; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciinline void rxe_queue_reset(struct rxe_queue *q) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci /* queue is comprised from header and the memory 4962306a36Sopenharmony_ci * of the actual queue. See "struct rxe_queue_buf" in rxe_queue.h 5062306a36Sopenharmony_ci * reset only the queue itself and not the management header 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci memset(q->buf->data, 0, q->buf_size - sizeof(struct rxe_queue_buf)); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct rxe_queue *rxe_queue_init(struct rxe_dev *rxe, int *num_elem, 5662306a36Sopenharmony_ci unsigned int elem_size, enum queue_type type) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct rxe_queue *q; 5962306a36Sopenharmony_ci size_t buf_size; 6062306a36Sopenharmony_ci unsigned int num_slots; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* num_elem == 0 is allowed, but uninteresting */ 6362306a36Sopenharmony_ci if (*num_elem < 0) 6462306a36Sopenharmony_ci return NULL; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci q = kzalloc(sizeof(*q), GFP_KERNEL); 6762306a36Sopenharmony_ci if (!q) 6862306a36Sopenharmony_ci return NULL; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci q->rxe = rxe; 7162306a36Sopenharmony_ci q->type = type; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* used in resize, only need to copy used part of queue */ 7462306a36Sopenharmony_ci q->elem_size = elem_size; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* pad element up to at least a cacheline and always a power of 2 */ 7762306a36Sopenharmony_ci if (elem_size < cache_line_size()) 7862306a36Sopenharmony_ci elem_size = cache_line_size(); 7962306a36Sopenharmony_ci elem_size = roundup_pow_of_two(elem_size); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci q->log2_elem_size = order_base_2(elem_size); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci num_slots = *num_elem + 1; 8462306a36Sopenharmony_ci num_slots = roundup_pow_of_two(num_slots); 8562306a36Sopenharmony_ci q->index_mask = num_slots - 1; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci buf_size = sizeof(struct rxe_queue_buf) + num_slots * elem_size; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci q->buf = vmalloc_user(buf_size); 9062306a36Sopenharmony_ci if (!q->buf) 9162306a36Sopenharmony_ci goto err2; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci q->buf->log2_elem_size = q->log2_elem_size; 9462306a36Sopenharmony_ci q->buf->index_mask = q->index_mask; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci q->buf_size = buf_size; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci *num_elem = num_slots - 1; 9962306a36Sopenharmony_ci return q; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cierr2: 10262306a36Sopenharmony_ci kfree(q); 10362306a36Sopenharmony_ci return NULL; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* copies elements from original q to new q and then swaps the contents of the 10762306a36Sopenharmony_ci * two q headers. This is so that if anyone is holding a pointer to q it will 10862306a36Sopenharmony_ci * still work 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic int resize_finish(struct rxe_queue *q, struct rxe_queue *new_q, 11162306a36Sopenharmony_ci unsigned int num_elem) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci enum queue_type type = q->type; 11462306a36Sopenharmony_ci u32 new_prod; 11562306a36Sopenharmony_ci u32 prod; 11662306a36Sopenharmony_ci u32 cons; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!queue_empty(q, q->type) && (num_elem < queue_count(q, type))) 11962306a36Sopenharmony_ci return -EINVAL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci new_prod = queue_get_producer(new_q, type); 12262306a36Sopenharmony_ci prod = queue_get_producer(q, type); 12362306a36Sopenharmony_ci cons = queue_get_consumer(q, type); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci while ((prod - cons) & q->index_mask) { 12662306a36Sopenharmony_ci memcpy(queue_addr_from_index(new_q, new_prod), 12762306a36Sopenharmony_ci queue_addr_from_index(q, cons), new_q->elem_size); 12862306a36Sopenharmony_ci new_prod = queue_next_index(new_q, new_prod); 12962306a36Sopenharmony_ci cons = queue_next_index(q, cons); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci new_q->buf->producer_index = new_prod; 13362306a36Sopenharmony_ci q->buf->consumer_index = cons; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* update private index copies */ 13662306a36Sopenharmony_ci if (type == QUEUE_TYPE_TO_CLIENT) 13762306a36Sopenharmony_ci new_q->index = new_q->buf->producer_index; 13862306a36Sopenharmony_ci else 13962306a36Sopenharmony_ci q->index = q->buf->consumer_index; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* exchange rxe_queue headers */ 14262306a36Sopenharmony_ci swap(*q, *new_q); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciint rxe_queue_resize(struct rxe_queue *q, unsigned int *num_elem_p, 14862306a36Sopenharmony_ci unsigned int elem_size, struct ib_udata *udata, 14962306a36Sopenharmony_ci struct mminfo __user *outbuf, spinlock_t *producer_lock, 15062306a36Sopenharmony_ci spinlock_t *consumer_lock) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct rxe_queue *new_q; 15362306a36Sopenharmony_ci unsigned int num_elem = *num_elem_p; 15462306a36Sopenharmony_ci int err; 15562306a36Sopenharmony_ci unsigned long producer_flags; 15662306a36Sopenharmony_ci unsigned long consumer_flags; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci new_q = rxe_queue_init(q->rxe, &num_elem, elem_size, q->type); 15962306a36Sopenharmony_ci if (!new_q) 16062306a36Sopenharmony_ci return -ENOMEM; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci err = do_mmap_info(new_q->rxe, outbuf, udata, new_q->buf, 16362306a36Sopenharmony_ci new_q->buf_size, &new_q->ip); 16462306a36Sopenharmony_ci if (err) { 16562306a36Sopenharmony_ci vfree(new_q->buf); 16662306a36Sopenharmony_ci kfree(new_q); 16762306a36Sopenharmony_ci goto err1; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci spin_lock_irqsave(consumer_lock, consumer_flags); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (producer_lock) { 17362306a36Sopenharmony_ci spin_lock_irqsave(producer_lock, producer_flags); 17462306a36Sopenharmony_ci err = resize_finish(q, new_q, num_elem); 17562306a36Sopenharmony_ci spin_unlock_irqrestore(producer_lock, producer_flags); 17662306a36Sopenharmony_ci } else { 17762306a36Sopenharmony_ci err = resize_finish(q, new_q, num_elem); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci spin_unlock_irqrestore(consumer_lock, consumer_flags); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci rxe_queue_cleanup(new_q); /* new/old dep on err */ 18362306a36Sopenharmony_ci if (err) 18462306a36Sopenharmony_ci goto err1; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci *num_elem_p = num_elem; 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cierr1: 19062306a36Sopenharmony_ci return err; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_civoid rxe_queue_cleanup(struct rxe_queue *q) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci if (q->ip) 19662306a36Sopenharmony_ci kref_put(&q->ip->ref, rxe_mmap_release); 19762306a36Sopenharmony_ci else 19862306a36Sopenharmony_ci vfree(q->buf); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci kfree(q); 20162306a36Sopenharmony_ci} 202