blob: b5d6bb28bd452785212b98cb62b066b95e144057 [file] [edit]
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2024, Microsoft Corporation. All rights reserved.
*/
#ifndef _MANA_SHADOW_QUEUE_H_
#define _MANA_SHADOW_QUEUE_H_
#include <linux/build_bug.h>
#define MANA_WQ_FENCE_WC BIT(0)
#define MANA_WQ_NO_SIGNAL_WC BIT(1)
#define MANA_WQE_OFFSET_MASK GENMASK(23, 0)
struct shadow_wqe_header {
u64 wr_id;
u64 wqe_offset_or_psn : 24;
u64 wqe_size_in_bu : 8;
u64 fsn : 24;
u64 send_opcode : 4;
u64 flags : 2;
};
struct shadow_queue {
/* Unmasked producer index, Incremented on wqe posting */
u64 prod_idx;
/* Unmasked consumer index, Incremented on cq polling */
u64 cons_idx;
/* queue size in wqes */
u32 length;
/* distance between elements in bytes */
u32 stride;
/* ring buffer holding wqes */
void *buffer;
};
static inline int create_shadow_queue(struct shadow_queue *queue, uint32_t length, uint32_t stride)
{
queue->buffer = kvmalloc_array(length, stride, GFP_KERNEL);
if (!queue->buffer)
return -ENOMEM;
queue->length = length;
queue->stride = stride;
return 0;
}
static inline void reset_shadow_queue(struct shadow_queue *queue)
{
queue->prod_idx = 0;
queue->cons_idx = 0;
}
static inline void destroy_shadow_queue(struct shadow_queue *queue)
{
kvfree(queue->buffer);
}
static inline bool shadow_queue_full(struct shadow_queue *queue)
{
/* Do not reuse an entry until the poller has finished reading it. */
return (queue->prod_idx - smp_load_acquire(&queue->cons_idx)) >= queue->length;
}
static inline bool shadow_queue_empty(struct shadow_queue *queue)
{
/* Pair with posting's release of the initialized shadow WQE. */
return smp_load_acquire(&queue->prod_idx) == queue->cons_idx;
}
static inline void *
shadow_queue_get_element(const struct shadow_queue *queue, u64 unmasked_index)
{
u32 index = unmasked_index % queue->length;
return ((u8 *)queue->buffer + index * queue->stride);
}
static inline void *
shadow_queue_producer_entry(struct shadow_queue *queue)
{
return shadow_queue_get_element(queue, queue->prod_idx);
}
static inline void *
shadow_queue_get_next_to_consume(const struct shadow_queue *queue)
{
/* The producer publishes the WQE before advancing prod_idx. */
if (queue->cons_idx == smp_load_acquire(&queue->prod_idx))
return NULL;
return shadow_queue_get_element(queue, queue->cons_idx);
}
static inline void shadow_queue_advance_producer(struct shadow_queue *queue)
{
/* Publish all WQE fields to CQ polling on another CPU. */
smp_store_release(&queue->prod_idx, queue->prod_idx + 1);
}
static inline void shadow_queue_advance_consumer(struct shadow_queue *queue)
{
/* Finish WC generation and queue-tail updates before allowing reuse. */
smp_store_release(&queue->cons_idx, queue->cons_idx + 1);
}
#endif