blob: 095ec7b1399d94e2c361408b62670650f41b85af [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2019 Mellanox Technologies. */
#include <linux/smp.h>
#include "dr_types.h"
#define QUEUE_SIZE 128
#define SIGNAL_PER_DIV_QUEUE 16
#define TH_NUMS_TO_DRAIN 2
enum { CQ_OK = 0, CQ_EMPTY = -1, CQ_POLL_ERR = -2 };
struct dr_data_seg {
u64 addr;
u32 length;
u32 lkey;
unsigned int send_flags;
};
struct postsend_info {
struct dr_data_seg write;
struct dr_data_seg read;
u64 remote_addr;
u32 rkey;
};
struct dr_qp_rtr_attr {
struct mlx5dr_cmd_gid_attr dgid_attr;
enum ib_mtu mtu;
u32 qp_num;
u16 port_num;
u8 min_rnr_timer;
u8 sgid_index;
u16 udp_src_port;
};
struct dr_qp_rts_attr {
u8 timeout;
u8 retry_cnt;
u8 rnr_retry;
};
struct dr_qp_init_attr {
u32 cqn;
u32 pdn;
u32 max_send_wr;
struct mlx5_uars_page *uar;
};
static int dr_parse_cqe(struct mlx5dr_cq *dr_cq, struct mlx5_cqe64 *cqe64)
{
unsigned int idx;
u8 opcode;
opcode = get_cqe_opcode(cqe64);
if (opcode == MLX5_CQE_REQ_ERR) {
idx = be16_to_cpu(cqe64->wqe_counter) &
(dr_cq->qp->sq.wqe_cnt - 1);
dr_cq->qp->sq.cc = dr_cq->qp->sq.wqe_head[idx] + 1;
} else if (opcode == MLX5_CQE_RESP_ERR) {
++dr_cq->qp->sq.cc;
} else {
idx = be16_to_cpu(cqe64->wqe_counter) &
(dr_cq->qp->sq.wqe_cnt - 1);
dr_cq->qp->sq.cc = dr_cq->qp->sq.wqe_head[idx] + 1;
return CQ_OK;
}
return CQ_POLL_ERR;
}
static int dr_cq_poll_one(struct mlx5dr_cq *dr_cq)
{
struct mlx5_cqe64 *cqe64;
int err;
cqe64 = mlx5_cqwq_get_cqe(&dr_cq->wq);
if (!cqe64)
return CQ_EMPTY;
mlx5_cqwq_pop(&dr_cq->wq);
err = dr_parse_cqe(dr_cq, cqe64);
mlx5_cqwq_update_db_record(&dr_cq->wq);
return err;
}
static int dr_poll_cq(struct mlx5dr_cq *dr_cq, int ne)
{
int npolled;
int err = 0;
for (npolled = 0; npolled < ne; ++npolled) {
err = dr_cq_poll_one(dr_cq);
if (err != CQ_OK)
break;
}
return err == CQ_POLL_ERR ? err : npolled;
}
static void dr_qp_event(struct mlx5_core_qp *mqp, int event)
{
pr_info("DR QP event %u on QP #%u\n", event, mqp->qpn);
}
static struct mlx5dr_qp *dr_create_rc_qp(struct mlx5_core_dev *mdev,
struct dr_qp_init_attr *attr)
{
u32 temp_qpc[MLX5_ST_SZ_DW(qpc)] = {};
struct mlx5_wq_param wqp;
struct mlx5dr_qp *dr_qp;
int inlen;
void *qpc;
void *in;
int err;
dr_qp = kzalloc(sizeof(*dr_qp), GFP_KERNEL);
if (!dr_qp)
return NULL;
wqp.buf_numa_node = mdev->priv.numa_node;
wqp.db_numa_node = mdev->priv.numa_node;
dr_qp->rq.pc = 0;
dr_qp->rq.cc = 0;
dr_qp->rq.wqe_cnt = 4;
dr_qp->sq.pc = 0;
dr_qp->sq.cc = 0;
dr_qp->sq.wqe_cnt = roundup_pow_of_two(attr->max_send_wr);
MLX5_SET(qpc, temp_qpc, log_rq_stride, ilog2(MLX5_SEND_WQE_DS) - 4);
MLX5_SET(qpc, temp_qpc, log_rq_size, ilog2(dr_qp->rq.wqe_cnt));
MLX5_SET(qpc, temp_qpc, log_sq_size, ilog2(dr_qp->sq.wqe_cnt));
err = mlx5_wq_qp_create(mdev, &wqp, temp_qpc, &dr_qp->wq,
&dr_qp->wq_ctrl);
if (err) {
mlx5_core_info(mdev, "Can't create QP WQ\n");
goto err_wq;
}
dr_qp->sq.wqe_head = kcalloc(dr_qp->sq.wqe_cnt,
sizeof(dr_qp->sq.wqe_head[0]),
GFP_KERNEL);
if (!dr_qp->sq.wqe_head) {
mlx5_core_warn(mdev, "Can't allocate wqe head\n");
goto err_wqe_head;
}
inlen = MLX5_ST_SZ_BYTES(create_qp_in) +
MLX5_FLD_SZ_BYTES(create_qp_in, pas[0]) *
dr_qp->wq_ctrl.buf.npages;
in = kvzalloc(inlen, GFP_KERNEL);
if (!in) {
err = -ENOMEM;
goto err_in;
}
qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);
MLX5_SET(qpc, qpc, st, MLX5_QP_ST_RC);
MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
MLX5_SET(qpc, qpc, pd, attr->pdn);
MLX5_SET(qpc, qpc, uar_page, attr->uar->index);
MLX5_SET(qpc, qpc, log_page_size,
dr_qp->wq_ctrl.buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
MLX5_SET(qpc, qpc, fre, 1);
MLX5_SET(qpc, qpc, rlky, 1);
MLX5_SET(qpc, qpc, cqn_snd, attr->cqn);
MLX5_SET(qpc, qpc, cqn_rcv, attr->cqn);
MLX5_SET(qpc, qpc, log_rq_stride, ilog2(MLX5_SEND_WQE_DS) - 4);
MLX5_SET(qpc, qpc, log_rq_size, ilog2(dr_qp->rq.wqe_cnt));
MLX5_SET(qpc, qpc, rq_type, MLX5_NON_ZERO_RQ);
MLX5_SET(qpc, qpc, log_sq_size, ilog2(dr_qp->sq.wqe_cnt));
MLX5_SET64(qpc, qpc, dbr_addr, dr_qp->wq_ctrl.db.dma);
if (MLX5_CAP_GEN(mdev, cqe_version) == 1)
MLX5_SET(qpc, qpc, user_index, 0xFFFFFF);
mlx5_fill_page_frag_array(&dr_qp->wq_ctrl.buf,
(__be64 *)MLX5_ADDR_OF(create_qp_in,
in, pas));
err = mlx5_core_create_qp(mdev, &dr_qp->mqp, in, inlen);
kfree(in);
if (err) {
mlx5_core_warn(mdev, " Can't create QP\n");
goto err_in;
}
dr_qp->mqp.event = dr_qp_event;
dr_qp->uar = attr->uar;
return dr_qp;
err_in:
kfree(dr_qp->sq.wqe_head);
err_wqe_head:
mlx5_wq_destroy(&dr_qp->wq_ctrl);
err_wq:
kfree(dr_qp);
return NULL;
}
static void dr_destroy_qp(struct mlx5_core_dev *mdev,
struct mlx5dr_qp *dr_qp)
{
mlx5_core_destroy_qp(mdev, &dr_qp->mqp);
kfree(dr_qp->sq.wqe_head);
mlx5_wq_destroy(&dr_qp->wq_ctrl);
kfree(dr_qp);
}
static void dr_cmd_notify_hw(struct mlx5dr_qp *dr_qp, void *ctrl)
{
dma_wmb();
*dr_qp->wq.sq.db = cpu_to_be32(dr_qp->sq.pc & 0xfffff);
/* After wmb() the hw aware of new work */
wmb();
mlx5_write64(ctrl, dr_qp->uar->map + MLX5_BF_OFFSET);
}
static void dr_rdma_segments(struct mlx5dr_qp *dr_qp, u64 remote_addr,
u32 rkey, struct dr_data_seg *data_seg,
u32 opcode, int nreq)
{
struct mlx5_wqe_raddr_seg *wq_raddr;
struct mlx5_wqe_ctrl_seg *wq_ctrl;
struct mlx5_wqe_data_seg *wq_dseg;
unsigned int size;
unsigned int idx;
size = sizeof(*wq_ctrl) / 16 + sizeof(*wq_dseg) / 16 +
sizeof(*wq_raddr) / 16;
idx = dr_qp->sq.pc & (dr_qp->sq.wqe_cnt - 1);
wq_ctrl = mlx5_wq_cyc_get_wqe(&dr_qp->wq.sq, idx);
wq_ctrl->imm = 0;
wq_ctrl->fm_ce_se = (data_seg->send_flags) ?
MLX5_WQE_CTRL_CQ_UPDATE : 0;
wq_ctrl->opmod_idx_opcode = cpu_to_be32(((dr_qp->sq.pc & 0xffff) << 8) |
opcode);
wq_ctrl->qpn_ds = cpu_to_be32(size | dr_qp->mqp.qpn << 8);
wq_raddr = (void *)(wq_ctrl + 1);
wq_raddr->raddr = cpu_to_be64(remote_addr);
wq_raddr->rkey = cpu_to_be32(rkey);
wq_raddr->reserved = 0;
wq_dseg = (void *)(wq_raddr + 1);
wq_dseg->byte_count = cpu_to_be32(data_seg->length);
wq_dseg->lkey = cpu_to_be32(data_seg->lkey);
wq_dseg->addr = cpu_to_be64(data_seg->addr);
dr_qp->sq.wqe_head[idx] = dr_qp->sq.pc++;
if (nreq)
dr_cmd_notify_hw(dr_qp, wq_ctrl);
}
static void dr_post_send(struct mlx5dr_qp *dr_qp, struct postsend_info *send_info)
{
dr_rdma_segments(dr_qp, send_info->remote_addr, send_info->rkey,
&send_info->write, MLX5_OPCODE_RDMA_WRITE, 0);
dr_rdma_segments(dr_qp, send_info->remote_addr, send_info->rkey,
&send_info->read, MLX5_OPCODE_RDMA_READ, 1);
}
/**
* mlx5dr_send_fill_and_append_ste_send_info: Add data to be sent
* with send_list parameters:
*
* @ste: The data that attached to this specific ste
* @size: of data to write
* @offset: of the data from start of the hw_ste entry
* @data: data
* @ste_info: ste to be sent with send_list
* @send_list: to append into it
* @copy_data: if true indicates that the data should be kept because
* it's not backuped any where (like in re-hash).
* if false, it lets the data to be updated after
* it was added to the list.
*/
void mlx5dr_send_fill_and_append_ste_send_info(struct mlx5dr_ste *ste, u16 size,
u16 offset, u8 *data,
struct mlx5dr_ste_send_info *ste_info,
struct list_head *send_list,
bool copy_data)
{
ste_info->size = size;
ste_info->ste = ste;
ste_info->offset = offset;
if (copy_data) {
memcpy(ste_info->data_cont, data, size);
ste_info->data = ste_info->data_cont;
} else {
ste_info->data = data;
}
list_add_tail(&ste_info->send_list, send_list);
}
/* The function tries to consume one wc each time, unless the queue is full, in
* that case, which means that the hw is behind the sw in a full queue len
* the function will drain the cq till it empty.
*/
static int dr_handle_pending_wc(struct mlx5dr_domain *dmn,
struct mlx5dr_send_ring *send_ring)
{
bool is_drain = false;
int ne;
if (send_ring->pending_wqe < send_ring->signal_th)
return 0;
/* Queue is full start drain it */
if (send_ring->pending_wqe >=
dmn->send_ring->signal_th * TH_NUMS_TO_DRAIN)
is_drain = true;
do {
ne = dr_poll_cq(send_ring->cq, 1);
if (ne < 0)
return ne;
else if (ne == 1)
send_ring->pending_wqe -= send_ring->signal_th;
} while (is_drain && send_ring->pending_wqe);
return 0;
}
static void dr_fill_data_segs(struct mlx5dr_send_ring *send_ring,
struct postsend_info *send_info)
{
send_ring->pending_wqe++;
if (send_ring->pending_wqe % send_ring->signal_th == 0)
send_info->write.send_flags |= IB_SEND_SIGNALED;
send_ring->pending_wqe++;
send_info->read.length = send_info->write.length;
/* Read into the same write area */
send_info->read.addr = (uintptr_t)send_info->write.addr;
send_info->read.lkey = send_ring->mr->mkey.key;
if (send_ring->pending_wqe % send_ring->signal_th == 0)
send_info->read.send_flags = IB_SEND_SIGNALED;
else
send_info->read.send_flags = 0;
}
static int dr_postsend_icm_data(struct mlx5dr_domain *dmn,
struct postsend_info *send_info)
{
struct mlx5dr_send_ring *send_ring = dmn->send_ring;
u32 buff_offset;
int ret;
ret = dr_handle_pending_wc(dmn, send_ring);
if (ret)
return ret;
if (send_info->write.length > dmn->info.max_inline_size) {
buff_offset = (send_ring->tx_head &
(dmn->send_ring->signal_th - 1)) *
send_ring->max_post_send_size;
/* Copy to ring mr */
memcpy(send_ring->buf + buff_offset,
(void *)(uintptr_t)send_info->write.addr,
send_info->write.length);
send_info->write.addr = (uintptr_t)send_ring->mr->dma_addr + buff_offset;
send_info->write.lkey = send_ring->mr->mkey.key;
}
send_ring->tx_head++;
dr_fill_data_segs(send_ring, send_info);
dr_post_send(send_ring->qp, send_info);
return 0;
}
static int dr_get_tbl_copy_details(struct mlx5dr_domain *dmn,
struct mlx5dr_ste_htbl *htbl,
u8 **data,
u32 *byte_size,
int *iterations,
int *num_stes)
{
int alloc_size;
if (htbl->chunk->byte_size > dmn->send_ring->max_post_send_size) {
*iterations = htbl->chunk->byte_size /
dmn->send_ring->max_post_send_size;
*byte_size = dmn->send_ring->max_post_send_size;
alloc_size = *byte_size;
*num_stes = *byte_size / DR_STE_SIZE;
} else {
*iterations = 1;
*num_stes = htbl->chunk->num_of_entries;
alloc_size = *num_stes * DR_STE_SIZE;
}
*data = kzalloc(alloc_size, GFP_KERNEL);
if (!*data)
return -ENOMEM;
return 0;
}
/**
* mlx5dr_send_postsend_ste: write size bytes into offset from the hw cm.
*
* @dmn: Domain
* @ste: The ste struct that contains the data (at
* least part of it)
* @data: The real data to send size data
* @size: for writing.
* @offset: The offset from the icm mapped data to
* start write to this for write only part of the
* buffer.
*
* Return: 0 on success.
*/
int mlx5dr_send_postsend_ste(struct mlx5dr_domain *dmn, struct mlx5dr_ste *ste,
u8 *data, u16 size, u16 offset)
{
struct postsend_info send_info = {};
send_info.write.addr = (uintptr_t)data;
send_info.write.length = size;
send_info.write.lkey = 0;
send_info.remote_addr = mlx5dr_ste_get_mr_addr(ste) + offset;
send_info.rkey = ste->htbl->chunk->rkey;
return dr_postsend_icm_data(dmn, &send_info);
}
int mlx5dr_send_postsend_htbl(struct mlx5dr_domain *dmn,
struct mlx5dr_ste_htbl *htbl,
u8 *formatted_ste, u8 *mask)
{
u32 byte_size = htbl->chunk->byte_size;
int num_stes_per_iter;
int iterations;
u8 *data;
int ret;
int i;
int j;
ret = dr_get_tbl_copy_details(dmn, htbl, &data, &byte_size,
&iterations, &num_stes_per_iter);
if (ret)
return ret;
/* Send the data iteration times */
for (i = 0; i < iterations; i++) {
u32 ste_index = i * (byte_size / DR_STE_SIZE);
struct postsend_info send_info = {};
/* Copy all ste's on the data buffer
* need to add the bit_mask
*/
for (j = 0; j < num_stes_per_iter; j++) {
u8 *hw_ste = htbl->ste_arr[ste_index + j].hw_ste;
u32 ste_off = j * DR_STE_SIZE;
if (mlx5dr_ste_is_not_valid_entry(hw_ste)) {
memcpy(data + ste_off,
formatted_ste, DR_STE_SIZE);
} else {
/* Copy data */
memcpy(data + ste_off,
htbl->ste_arr[ste_index + j].hw_ste,
DR_STE_SIZE_REDUCED);
/* Copy bit_mask */
memcpy(data + ste_off + DR_STE_SIZE_REDUCED,
mask, DR_STE_SIZE_MASK);
}
}
send_info.write.addr = (uintptr_t)data;
send_info.write.length = byte_size;
send_info.write.lkey = 0;
send_info.remote_addr =
mlx5dr_ste_get_mr_addr(htbl->ste_arr + ste_index);
send_info.rkey = htbl->chunk->rkey;
ret = dr_postsend_icm_data(dmn, &send_info);
if (ret)
goto out_free;
}
out_free:
kfree(data);
return ret;
}
/* Initialize htble with default STEs */
int mlx5dr_send_postsend_formatted_htbl(struct mlx5dr_domain *dmn,
struct mlx5dr_ste_htbl *htbl,
u8 *ste_init_data,
bool update_hw_ste)
{
u32 byte_size = htbl->chunk->byte_size;
int iterations;
int num_stes;
u8 *data;
int ret;
int i;
ret = dr_get_tbl_copy_details(dmn, htbl, &data, &byte_size,
&iterations, &num_stes);
if (ret)
return ret;
for (i = 0; i < num_stes; i++) {
u8 *copy_dst;
/* Copy the same ste on the data buffer */
copy_dst = data + i * DR_STE_SIZE;
memcpy(copy_dst, ste_init_data, DR_STE_SIZE);
if (update_hw_ste) {
/* Copy the reduced ste to hash table ste_arr */
copy_dst = htbl->hw_ste_arr + i * DR_STE_SIZE_REDUCED;
memcpy(copy_dst, ste_init_data, DR_STE_SIZE_REDUCED);
}
}
/* Send the data iteration times */
for (i = 0; i < iterations; i++) {
u8 ste_index = i * (byte_size / DR_STE_SIZE);
struct postsend_info send_info = {};
send_info.write.addr = (uintptr_t)data;
send_info.write.length = byte_size;
send_info.write.lkey = 0;
send_info.remote_addr =
mlx5dr_ste_get_mr_addr(htbl->ste_arr + ste_index);
send_info.rkey = htbl->chunk->rkey;
ret = dr_postsend_icm_data(dmn, &send_info);
if (ret)
goto out_free;
}
out_free:
kfree(data);
return ret;
}
int mlx5dr_send_postsend_action(struct mlx5dr_domain *dmn,
struct mlx5dr_action *action)
{
struct postsend_info send_info = {};
int ret;
send_info.write.addr = (uintptr_t)action->rewrite.data;
send_info.write.length = action->rewrite.num_of_actions *
DR_MODIFY_ACTION_SIZE;
send_info.write.lkey = 0;
send_info.remote_addr = action->rewrite.chunk->mr_addr;
send_info.rkey = action->rewrite.chunk->rkey;
mutex_lock(&dmn->mutex);
ret = dr_postsend_icm_data(dmn, &send_info);
mutex_unlock(&dmn->mutex);
return ret;
}
static int dr_modify_qp_rst2init(struct mlx5_core_dev *mdev,
struct mlx5dr_qp *dr_qp,
int port)
{
u32 in[MLX5_ST_SZ_DW(rst2init_qp_in)] = {};
void *qpc;
qpc = MLX5_ADDR_OF(rst2init_qp_in, in, qpc);
MLX5_SET(qpc, qpc, primary_address_path.vhca_port_num, port);
MLX5_SET(qpc, qpc, pm_state, MLX5_QPC_PM_STATE_MIGRATED);
MLX5_SET(qpc, qpc, rre, 1);
MLX5_SET(qpc, qpc, rwe, 1);
return mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RST2INIT_QP, 0, qpc,
&dr_qp->mqp);
}
static int dr_cmd_modify_qp_rtr2rts(struct mlx5_core_dev *mdev,
struct mlx5dr_qp *dr_qp,
struct dr_qp_rts_attr *attr)
{
u32 in[MLX5_ST_SZ_DW(rtr2rts_qp_in)] = {};
void *qpc;
qpc = MLX5_ADDR_OF(rtr2rts_qp_in, in, qpc);
MLX5_SET(rtr2rts_qp_in, in, qpn, dr_qp->mqp.qpn);
MLX5_SET(qpc, qpc, log_ack_req_freq, 0);
MLX5_SET(qpc, qpc, retry_count, attr->retry_cnt);
MLX5_SET(qpc, qpc, rnr_retry, attr->rnr_retry);
return mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RTR2RTS_QP, 0, qpc,
&dr_qp->mqp);
}
static int dr_cmd_modify_qp_init2rtr(struct mlx5_core_dev *mdev,
struct mlx5dr_qp *dr_qp,
struct dr_qp_rtr_attr *attr)
{
u32 in[MLX5_ST_SZ_DW(init2rtr_qp_in)] = {};
void *qpc;
qpc = MLX5_ADDR_OF(init2rtr_qp_in, in, qpc);
MLX5_SET(init2rtr_qp_in, in, qpn, dr_qp->mqp.qpn);
MLX5_SET(qpc, qpc, mtu, attr->mtu);
MLX5_SET(qpc, qpc, log_msg_max, DR_CHUNK_SIZE_MAX - 1);
MLX5_SET(qpc, qpc, remote_qpn, attr->qp_num);
memcpy(MLX5_ADDR_OF(qpc, qpc, primary_address_path.rmac_47_32),
attr->dgid_attr.mac, sizeof(attr->dgid_attr.mac));
memcpy(MLX5_ADDR_OF(qpc, qpc, primary_address_path.rgid_rip),
attr->dgid_attr.gid, sizeof(attr->dgid_attr.gid));
MLX5_SET(qpc, qpc, primary_address_path.src_addr_index,
attr->sgid_index);
if (attr->dgid_attr.roce_ver == MLX5_ROCE_VERSION_2)
MLX5_SET(qpc, qpc, primary_address_path.udp_sport,
attr->udp_src_port);
MLX5_SET(qpc, qpc, primary_address_path.vhca_port_num, attr->port_num);
MLX5_SET(qpc, qpc, min_rnr_nak, 1);
return mlx5_core_qp_modify(mdev, MLX5_CMD_OP_INIT2RTR_QP, 0, qpc,
&dr_qp->mqp);
}
static int dr_prepare_qp_to_rts(struct mlx5dr_domain *dmn)
{
struct mlx5dr_qp *dr_qp = dmn->send_ring->qp;
struct dr_qp_rts_attr rts_attr = {};
struct dr_qp_rtr_attr rtr_attr = {};
enum ib_mtu mtu = IB_MTU_1024;
u16 gid_index = 0;
int port = 1;
int ret;
/* Init */
ret = dr_modify_qp_rst2init(dmn->mdev, dr_qp, port);
if (ret)
return ret;
/* RTR */
ret = mlx5dr_cmd_query_gid(dmn->mdev, port, gid_index, &rtr_attr.dgid_attr);
if (ret)
return ret;
rtr_attr.mtu = mtu;
rtr_attr.qp_num = dr_qp->mqp.qpn;
rtr_attr.min_rnr_timer = 12;
rtr_attr.port_num = port;
rtr_attr.sgid_index = gid_index;
rtr_attr.udp_src_port = dmn->info.caps.roce_min_src_udp;
ret = dr_cmd_modify_qp_init2rtr(dmn->mdev, dr_qp, &rtr_attr);
if (ret)
return ret;
/* RTS */
rts_attr.timeout = 14;
rts_attr.retry_cnt = 7;
rts_attr.rnr_retry = 7;
ret = dr_cmd_modify_qp_rtr2rts(dmn->mdev, dr_qp, &rts_attr);
if (ret)
return ret;
return 0;
}
static void dr_cq_event(struct mlx5_core_cq *mcq,
enum mlx5_event event)
{
pr_info("CQ event %u on CQ #%u\n", event, mcq->cqn);
}
static struct mlx5dr_cq *dr_create_cq(struct mlx5_core_dev *mdev,
struct mlx5_uars_page *uar,
size_t ncqe)
{
u32 temp_cqc[MLX5_ST_SZ_DW(cqc)] = {};
u32 out[MLX5_ST_SZ_DW(create_cq_out)];
struct mlx5_wq_param wqp;
struct mlx5_cqe64 *cqe;
struct mlx5dr_cq *cq;
int inlen, err, eqn;
unsigned int irqn;
void *cqc, *in;
__be64 *pas;
int vector;
u32 i;
cq = kzalloc(sizeof(*cq), GFP_KERNEL);
if (!cq)
return NULL;
ncqe = roundup_pow_of_two(ncqe);
MLX5_SET(cqc, temp_cqc, log_cq_size, ilog2(ncqe));
wqp.buf_numa_node = mdev->priv.numa_node;
wqp.db_numa_node = mdev->priv.numa_node;
err = mlx5_cqwq_create(mdev, &wqp, temp_cqc, &cq->wq,
&cq->wq_ctrl);
if (err)
goto out;
for (i = 0; i < mlx5_cqwq_get_size(&cq->wq); i++) {
cqe = mlx5_cqwq_get_wqe(&cq->wq, i);
cqe->op_own = MLX5_CQE_INVALID << 4 | MLX5_CQE_OWNER_MASK;
}
inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
sizeof(u64) * cq->wq_ctrl.buf.npages;
in = kvzalloc(inlen, GFP_KERNEL);
if (!in)
goto err_cqwq;
vector = raw_smp_processor_id() % mlx5_comp_vectors_count(mdev);
err = mlx5_vector2eqn(mdev, vector, &eqn, &irqn);
if (err) {
kvfree(in);
goto err_cqwq;
}
cqc = MLX5_ADDR_OF(create_cq_in, in, cq_context);
MLX5_SET(cqc, cqc, log_cq_size, ilog2(ncqe));
MLX5_SET(cqc, cqc, c_eqn, eqn);
MLX5_SET(cqc, cqc, uar_page, uar->index);
MLX5_SET(cqc, cqc, log_page_size, cq->wq_ctrl.buf.page_shift -
MLX5_ADAPTER_PAGE_SHIFT);
MLX5_SET64(cqc, cqc, dbr_addr, cq->wq_ctrl.db.dma);
pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas);
mlx5_fill_page_frag_array(&cq->wq_ctrl.buf, pas);
cq->mcq.event = dr_cq_event;
err = mlx5_core_create_cq(mdev, &cq->mcq, in, inlen, out, sizeof(out));
kvfree(in);
if (err)
goto err_cqwq;
cq->mcq.cqe_sz = 64;
cq->mcq.set_ci_db = cq->wq_ctrl.db.db;
cq->mcq.arm_db = cq->wq_ctrl.db.db + 1;
*cq->mcq.set_ci_db = 0;
*cq->mcq.arm_db = 0;
cq->mcq.vector = 0;
cq->mcq.irqn = irqn;
cq->mcq.uar = uar;
return cq;
err_cqwq:
mlx5_wq_destroy(&cq->wq_ctrl);
out:
kfree(cq);
return NULL;
}
static void dr_destroy_cq(struct mlx5_core_dev *mdev, struct mlx5dr_cq *cq)
{
mlx5_core_destroy_cq(mdev, &cq->mcq);
mlx5_wq_destroy(&cq->wq_ctrl);
kfree(cq);
}
static int
dr_create_mkey(struct mlx5_core_dev *mdev, u32 pdn, struct mlx5_core_mkey *mkey)
{
u32 in[MLX5_ST_SZ_DW(create_mkey_in)] = {};
void *mkc;
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_PA);
MLX5_SET(mkc, mkc, a, 1);
MLX5_SET(mkc, mkc, rw, 1);
MLX5_SET(mkc, mkc, rr, 1);
MLX5_SET(mkc, mkc, lw, 1);
MLX5_SET(mkc, mkc, lr, 1);
MLX5_SET(mkc, mkc, pd, pdn);
MLX5_SET(mkc, mkc, length64, 1);
MLX5_SET(mkc, mkc, qpn, 0xffffff);
return mlx5_core_create_mkey(mdev, mkey, in, sizeof(in));
}
static struct mlx5dr_mr *dr_reg_mr(struct mlx5_core_dev *mdev,
u32 pdn, void *buf, size_t size)
{
struct mlx5dr_mr *mr = kzalloc(sizeof(*mr), GFP_KERNEL);
struct device *dma_device;
dma_addr_t dma_addr;
int err;
if (!mr)
return NULL;
dma_device = &mdev->pdev->dev;
dma_addr = dma_map_single(dma_device, buf, size,
DMA_BIDIRECTIONAL);
err = dma_mapping_error(dma_device, dma_addr);
if (err) {
mlx5_core_warn(mdev, "Can't dma buf\n");
kfree(mr);
return NULL;
}
err = dr_create_mkey(mdev, pdn, &mr->mkey);
if (err) {
mlx5_core_warn(mdev, "Can't create mkey\n");
dma_unmap_single(dma_device, dma_addr, size,
DMA_BIDIRECTIONAL);
kfree(mr);
return NULL;
}
mr->dma_addr = dma_addr;
mr->size = size;
mr->addr = buf;
return mr;
}
static void dr_dereg_mr(struct mlx5_core_dev *mdev, struct mlx5dr_mr *mr)
{
mlx5_core_destroy_mkey(mdev, &mr->mkey);
dma_unmap_single(&mdev->pdev->dev, mr->dma_addr, mr->size,
DMA_BIDIRECTIONAL);
kfree(mr);
}
int mlx5dr_send_ring_alloc(struct mlx5dr_domain *dmn)
{
struct dr_qp_init_attr init_attr = {};
int cq_size;
int size;
int ret;
dmn->send_ring = kzalloc(sizeof(*dmn->send_ring), GFP_KERNEL);
if (!dmn->send_ring)
return -ENOMEM;
cq_size = QUEUE_SIZE + 1;
dmn->send_ring->cq = dr_create_cq(dmn->mdev, dmn->uar, cq_size);
if (!dmn->send_ring->cq) {
ret = -ENOMEM;
goto free_send_ring;
}
init_attr.cqn = dmn->send_ring->cq->mcq.cqn;
init_attr.pdn = dmn->pdn;
init_attr.uar = dmn->uar;
init_attr.max_send_wr = QUEUE_SIZE;
dmn->send_ring->qp = dr_create_rc_qp(dmn->mdev, &init_attr);
if (!dmn->send_ring->qp) {
ret = -ENOMEM;
goto clean_cq;
}
dmn->send_ring->cq->qp = dmn->send_ring->qp;
dmn->info.max_send_wr = QUEUE_SIZE;
dmn->info.max_inline_size = min(dmn->send_ring->qp->max_inline_data,
DR_STE_SIZE);
dmn->send_ring->signal_th = dmn->info.max_send_wr /
SIGNAL_PER_DIV_QUEUE;
/* Prepare qp to be used */
ret = dr_prepare_qp_to_rts(dmn);
if (ret)
goto clean_qp;
dmn->send_ring->max_post_send_size =
mlx5dr_icm_pool_chunk_size_to_byte(DR_CHUNK_SIZE_1K,
DR_ICM_TYPE_STE);
/* Allocating the max size as a buffer for writing */
size = dmn->send_ring->signal_th * dmn->send_ring->max_post_send_size;
dmn->send_ring->buf = kzalloc(size, GFP_KERNEL);
if (!dmn->send_ring->buf) {
ret = -ENOMEM;
goto clean_qp;
}
dmn->send_ring->buf_size = size;
dmn->send_ring->mr = dr_reg_mr(dmn->mdev,
dmn->pdn, dmn->send_ring->buf, size);
if (!dmn->send_ring->mr) {
ret = -ENOMEM;
goto free_mem;
}
dmn->send_ring->sync_mr = dr_reg_mr(dmn->mdev,
dmn->pdn, dmn->send_ring->sync_buff,
MIN_READ_SYNC);
if (!dmn->send_ring->sync_mr) {
ret = -ENOMEM;
goto clean_mr;
}
return 0;
clean_mr:
dr_dereg_mr(dmn->mdev, dmn->send_ring->mr);
free_mem:
kfree(dmn->send_ring->buf);
clean_qp:
dr_destroy_qp(dmn->mdev, dmn->send_ring->qp);
clean_cq:
dr_destroy_cq(dmn->mdev, dmn->send_ring->cq);
free_send_ring:
kfree(dmn->send_ring);
return ret;
}
void mlx5dr_send_ring_free(struct mlx5dr_domain *dmn,
struct mlx5dr_send_ring *send_ring)
{
dr_destroy_qp(dmn->mdev, send_ring->qp);
dr_destroy_cq(dmn->mdev, send_ring->cq);
dr_dereg_mr(dmn->mdev, send_ring->sync_mr);
dr_dereg_mr(dmn->mdev, send_ring->mr);
kfree(send_ring->buf);
kfree(send_ring);
}
int mlx5dr_send_ring_force_drain(struct mlx5dr_domain *dmn)
{
struct mlx5dr_send_ring *send_ring = dmn->send_ring;
struct postsend_info send_info = {};
u8 data[DR_STE_SIZE];
int num_of_sends_req;
int ret;
int i;
/* Sending this amount of requests makes sure we will get drain */
num_of_sends_req = send_ring->signal_th * TH_NUMS_TO_DRAIN / 2;
/* Send fake requests forcing the last to be signaled */
send_info.write.addr = (uintptr_t)data;
send_info.write.length = DR_STE_SIZE;
send_info.write.lkey = 0;
/* Using the sync_mr in order to write/read */
send_info.remote_addr = (uintptr_t)send_ring->sync_mr->addr;
send_info.rkey = send_ring->sync_mr->mkey.key;
for (i = 0; i < num_of_sends_req; i++) {
ret = dr_postsend_icm_data(dmn, &send_info);
if (ret)
return ret;
}
ret = dr_handle_pending_wc(dmn, send_ring);
return ret;
}