/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
 * Copyright (c) 2022, Microsoft Corporation. All rights reserved.
 */

#ifndef _MANA_H_
#define _MANA_H_

#include "manadv.h"
#include <ccan/minmax.h>
#include "shadow_queue.h"
#include "gdma.h"

#define COMP_ENTRY_SIZE 64
#define MANA_IB_TOEPLITZ_HASH_KEY_SIZE_IN_BYTES 40

#define DMA_OOB_SIZE 8

#define INLINE_OOB_SMALL_SIZE 8
#define INLINE_OOB_LARGE_SIZE 24

#define GDMA_WQE_ALIGNMENT_UNIT_SIZE 32

/* The size of a SGE in WQE */
#define SGE_SIZE 16

#define DOORBELL_PAGE_SIZE 4096
#define MANA_PAGE_SIZE 4096

#define MANA_QP_TABLE_SIZE 4096
#define MANA_QP_TABLE_SHIFT 12
#define MANA_QP_TABLE_MASK (MANA_QP_TABLE_SIZE - 1)

/* PSN 24 bit arithmetic comparisons */
#define PSN_MASK 0xFFFFFF
#define PSN_SIGN_BIT 0x800000
#define PSN_GE(PSN1, PSN2) ((((PSN1) - (PSN2)) & PSN_SIGN_BIT) == 0)
#define PSN_GT(PSN1, PSN2) PSN_GE(PSN1, (PSN2) + 1)
#define PSN_LE(PSN1, PSN2) PSN_GE(PSN2, PSN1)
#define PSN_LT(PSN1, PSN2) PSN_GT(PSN2, PSN1)
#define MTU_SIZE(MTU) (1U << ((MTU) + 7))
#define PSN_DELTA(MSG_SIZE, MTU) max(1U, ((MSG_SIZE) + MTU_SIZE(MTU) - 1) >> (MTU + 7))
#define PSN_DEC(PSN) (((PSN) - 1) & PSN_MASK)
#define PSN_INC(PSN) (((PSN) + 1) & PSN_MASK)
#define PSN_ADD(PSN, DELTA) (((PSN) + (DELTA)) & PSN_MASK)

enum user_queue_types {
	USER_RNIC_SEND_QUEUE_REQUESTER = 0,
	USER_RNIC_SEND_QUEUE_RESPONDER = 1,
	USER_RNIC_RECV_QUEUE_REQUESTER = 2,
	USER_RNIC_RECV_QUEUE_RESPONDER = 3,
	USER_RNIC_QUEUE_TYPE_MAX = 4,
};

#define QUEUE_TYPE_MASK 0x3
#define QUEUE_TYPE_SREQ 0x0
#define QUEUE_TYPE_SRESP 0x1
#define QUEUE_TYPE_SMMQ 0x2
#define QUEUE_TYPE_RRESP 0x0
#define QUEUE_TYPE_RREQ 0x1

static inline uint32_t align_hw_size(uint32_t size)
{
	size = roundup_pow_of_two(size);
	return align(size, MANA_PAGE_SIZE);
}

static inline uint32_t get_wqe_size(uint32_t sge)
{
	uint32_t wqe_size = sge * SGE_SIZE + DMA_OOB_SIZE + INLINE_OOB_SMALL_SIZE;

	return align(wqe_size, GDMA_WQE_ALIGNMENT_UNIT_SIZE);
}

static inline uint32_t get_large_wqe_size(uint32_t sge)
{
	uint32_t wqe_size = sge * SGE_SIZE + DMA_OOB_SIZE + INLINE_OOB_LARGE_SIZE;

	return align(wqe_size, GDMA_WQE_ALIGNMENT_UNIT_SIZE);
}

struct mana_table {
	struct mana_qp **table;
	int refcnt;
};

struct mana_context {
	struct verbs_context ibv_ctx;
	struct mana_table qp_rtable[MANA_QP_TABLE_SIZE];
	struct mana_table qp_stable[MANA_QP_TABLE_SIZE];
	pthread_mutex_t qp_table_mutex;

	struct manadv_ctx_allocators extern_alloc;
	void *db_page;
};

struct mana_rwq_ind_table {
	struct ibv_rwq_ind_table ib_ind_table;

	uint32_t ind_tbl_size;
	struct ibv_wq **ind_tbl;
};

struct mana_gdma_queue {
	uint32_t id;
	uint32_t size;
	uint32_t prod_idx;
	uint32_t cons_idx;

	void *db_page;
	void *buffer;
};

struct mana_ib_raw_qp {
	void *send_buf;
	uint32_t send_buf_size;
	int send_wqe_count;
	uint32_t sqid;
	uint32_t tx_vp_offset;
};

struct mana_ib_rnic_qp {
	struct mana_gdma_queue queues[USER_RNIC_QUEUE_TYPE_MAX];
};

struct mana_qp {
	struct verbs_qp ibqp;
	pthread_spinlock_t sq_lock;
	pthread_spinlock_t rq_lock;
	uint32_t sq_ssn;
	uint32_t sq_psn;
	union {
		struct mana_ib_raw_qp raw_qp;
		struct mana_ib_rnic_qp rnic_qp;
	};

	enum ibv_mtu mtu;
	int sq_sig_all;

	struct shadow_queue shadow_rq;
	struct shadow_queue shadow_sq;

	struct list_node send_err_node;
	struct list_node recv_err_node;
	bool on_err_list_send;
	bool on_err_list_recv;
};

static inline struct mana_gdma_queue *mana_ib_get_rresp(struct mana_qp *qp)
{
	return &qp->rnic_qp.queues[USER_RNIC_RECV_QUEUE_RESPONDER];
}

static inline struct mana_gdma_queue *mana_ib_get_rreq(struct mana_qp *qp)
{
	return &qp->rnic_qp.queues[USER_RNIC_RECV_QUEUE_REQUESTER];
}

static inline struct mana_gdma_queue *mana_ib_get_sreq(struct mana_qp *qp)
{
	return &qp->rnic_qp.queues[USER_RNIC_SEND_QUEUE_REQUESTER];
}

struct mana_wq {
	struct ibv_wq ibwq;

	void *buf;
	uint32_t buf_size;

	uint32_t wqe;
	uint32_t sge;

	uint32_t wqid;
};

struct mana_cq {
	struct ibv_cq ibcq;
	uint32_t cqe;
	uint32_t cqid;
	void *buf;

	pthread_spinlock_t lock;
	uint32_t head;
	uint32_t poll_credit;
	void *db_page;
	struct list_head send_err_qp_list;
	struct list_head recv_err_qp_list;
	struct gdma_cqe pending_cqe;
	bool has_pending_cqe;
	bool buf_external;
};

struct mana_device {
	struct verbs_device verbs_dev;
};

struct mana_pd {
	struct ibv_pd ibv_pd;
	struct mana_pd *mprotection_domain;
};

struct mana_parent_domain {
	struct mana_pd mpd;
	void *pd_context;
};

struct mana_context *to_mctx(struct ibv_context *ibctx);

void *mana_alloc_mem(size_t size);
void mana_dealloc_mem(void *buf, size_t size);

int mana_query_device_ex(struct ibv_context *context,
			 const struct ibv_query_device_ex_input *input,
			 struct ibv_device_attr_ex *attr, size_t attr_size);

int mana_query_port(struct ibv_context *context, uint8_t port,
		    struct ibv_port_attr *attr);

struct ibv_pd *mana_alloc_pd(struct ibv_context *context);
struct ibv_pd *
mana_alloc_parent_domain(struct ibv_context *context,
			 struct ibv_parent_domain_init_attr *attr);

int mana_dealloc_pd(struct ibv_pd *pd);

struct ibv_mr *mana_reg_dmabuf_mr(struct ibv_pd *pd, uint64_t offset,
				  size_t length, uint64_t iova, int fd,
				  int access);

struct ibv_mr *mana_reg_mr(struct ibv_pd *pd, void *addr, size_t length,
			   uint64_t hca_va, int access);

struct ibv_dm *mana_alloc_dm(struct ibv_context *context,
			     struct ibv_alloc_dm_attr *dm_attr);
int mana_free_dm(struct ibv_dm *ibdm);
struct ibv_mr *mana_reg_dm_mr(struct ibv_pd *pd, struct ibv_dm *ibdm,
			      uint64_t dm_offset, size_t length,
			      unsigned int acc);

int mana_dereg_mr(struct verbs_mr *vmr);

struct ibv_cq *mana_create_cq(struct ibv_context *context, int cqe,
			      struct ibv_comp_channel *channel,
			      int comp_vector);

int mana_destroy_cq(struct ibv_cq *cq);

int mana_poll_cq(struct ibv_cq *ibcq, int nwc, struct ibv_wc *wc);

struct ibv_wq *mana_create_wq(struct ibv_context *context,
			      struct ibv_wq_init_attr *attr);

int mana_destroy_wq(struct ibv_wq *wq);
int mana_modify_wq(struct ibv_wq *ibwq, struct ibv_wq_attr *attr);

struct ibv_rwq_ind_table *
mana_create_rwq_ind_table(struct ibv_context *context,
			  struct ibv_rwq_ind_table_init_attr *init_attr);

int mana_destroy_rwq_ind_table(struct ibv_rwq_ind_table *rwq_ind_table);

struct ibv_qp *mana_create_qp(struct ibv_pd *pd, struct ibv_qp_init_attr *attr);

struct ibv_qp *mana_create_qp_ex(struct ibv_context *context,
				 struct ibv_qp_init_attr_ex *attr);

int mana_modify_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mask);

int mana_destroy_qp(struct ibv_qp *ibqp);

int mana_post_recv(struct ibv_qp *ibqp, struct ibv_recv_wr *wr,
		   struct ibv_recv_wr **bad);

int mana_post_send(struct ibv_qp *ibqp, struct ibv_send_wr *wr,
		   struct ibv_send_wr **bad);

int mana_arm_cq(struct ibv_cq *ibcq, int solicited);

struct mana_qp *mana_get_qp(struct mana_context *ctx, uint32_t qpn, bool is_sq);

void mana_qp_move_flush_err(struct ibv_qp *ibqp);
#endif
