/*
 * Copyright (c) 2009 Mellanox Technologies Ltd. All rights reserved.
 * Copyright (c) 2009 System Fabric Works, Inc. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the fileA
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

/* implements a simple circular buffer with sizes a power of 2 */

#ifndef H_RXE_QUEUE
#define H_RXE_QUEUE

#include <stdint.h>
#include <stdatomic.h>

#include "rxe.h"

/* N.B. producer_index and consumer_index always lie in the range
 * [0, index_mask] masking is only required when computing a new value.
 * Below, 'consumer_index lock' is cq->lock
 * and, 'producer_index lock' is one of rq, sq or srq->lock.
 * In the code below the only memory ordering required is between the
 * kernel driver (rdma_rxe) and the user provider library. Ordering between
 * user space threads is addressed by spinlocks which provide memory
 * barriers.
 */

typedef _Atomic(__u32) _atomic_t;

static inline _atomic_t *producer(struct rxe_queue_buf *q)
{
	return (_atomic_t *)&q->producer_index;
}

static inline _atomic_t *consumer(struct rxe_queue_buf *q)
{
	return (_atomic_t *)&q->consumer_index;
}

/* Must hold consumer_index lock (used by CQ only) */
static inline int queue_empty(struct rxe_queue_buf *q)
{
	__u32 prod;
	__u32 cons;

	prod = atomic_load_explicit(producer(q), memory_order_acquire);
	cons = atomic_load_explicit(consumer(q), memory_order_relaxed);

	return (prod == cons);
}

/* Must hold producer_index lock (used by SQ, RQ, SRQ only) */
static inline int queue_full(struct rxe_queue_buf *q)
{
	__u32 prod;
	__u32 cons;

	prod = atomic_load_explicit(producer(q), memory_order_relaxed);
	cons = atomic_load_explicit(consumer(q), memory_order_acquire);

	return (cons == ((prod + 1) & q->index_mask));
}

/* Must hold producer_index lock */
static inline void advance_producer(struct rxe_queue_buf *q)
{
	__u32 prod;

	prod = atomic_load_explicit(producer(q), memory_order_relaxed);
	prod = (prod + 1) & q->index_mask;

	atomic_store_explicit(producer(q), prod, memory_order_release);
}

/* Must hold consumer_index lock */
static inline void advance_consumer(struct rxe_queue_buf *q)
{
	__u32 cons;

	cons = atomic_load_explicit(consumer(q), memory_order_relaxed);
	cons = (cons + 1) & q->index_mask;

	atomic_store_explicit(consumer(q), cons, memory_order_release);
}

/* Must hold producer_index lock */
static inline __u32 load_producer_index(struct rxe_queue_buf *q)
{
	return atomic_load_explicit(producer(q), memory_order_relaxed);
}

/* Must hold producer_index lock */
static inline void store_producer_index(struct rxe_queue_buf *q, __u32 index)
{
	/* flush writes to work queue before moving index */
	atomic_store_explicit(producer(q), index, memory_order_release);
}

/* Must hold consumer_index lock */
static inline __u32 load_consumer_index(struct rxe_queue_buf *q)
{
	return atomic_load_explicit(consumer(q), memory_order_relaxed);
}

/* Must hold consumer_index lock */
static inline void store_consumer_index(struct rxe_queue_buf *q, __u32 index)
{
	/* complete reads from completion queue before moving index */
	atomic_store_explicit(consumer(q), index, memory_order_release);
}

/* Must hold producer_index lock */
static inline void *producer_addr(struct rxe_queue_buf *q)
{
	__u32 prod;

	prod = atomic_load_explicit(producer(q), memory_order_relaxed);

	return q->data + (prod << q->log2_elem_size);
}

/* Must hold consumer_index lock */
static inline void *consumer_addr(struct rxe_queue_buf *q)
{
	__u32 cons;

	cons = atomic_load_explicit(consumer(q), memory_order_relaxed);

	return q->data + (cons << q->log2_elem_size);
}

static inline void *addr_from_index(struct rxe_queue_buf *q,
				    unsigned int index)
{
	index &= q->index_mask;

	return q->data + (index << q->log2_elem_size);
}

static inline unsigned int index_from_addr(const struct rxe_queue_buf *q,
					   const void *addr)
{
	return (((__u8 *)addr - q->data) >> q->log2_elem_size) &
		q->index_mask;
}

static inline void advance_cq_cur_index(struct rxe_cq *cq)
{
	struct rxe_queue_buf *q = cq->queue;

	cq->cur_index = (cq->cur_index + 1) & q->index_mask;
}

static inline int check_cq_queue_empty(struct rxe_cq *cq)
{
	struct rxe_queue_buf *q = cq->queue;
	__u32 prod;

	prod = atomic_load_explicit(producer(q), memory_order_acquire);

	return (cq->cur_index == prod);
}

static inline void advance_qp_cur_index(struct rxe_qp *qp)
{
	struct rxe_queue_buf *q = qp->sq.queue;

	qp->cur_index = (qp->cur_index + 1) & q->index_mask;
}

static inline int check_qp_queue_full(struct rxe_qp *qp)
{
	struct rxe_queue_buf *q = qp->sq.queue;
	uint32_t cons;

	cons = atomic_load_explicit(consumer(q), memory_order_acquire);

	if (qp->err)
		goto err;

	if (cons == ((qp->cur_index + 1) & q->index_mask))
		qp->err = ENOSPC;
err:
	return qp->err;
}

#endif /* H_RXE_QUEUE */
