/* 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 #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