blob: 708e76ac4716fafcb20781ba64d061bf637f90a7 [file] [log] [blame]
/*
* 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 */