162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* Authors: Cheng Xu <chengyou@linux.alibaba.com> */ 462306a36Sopenharmony_ci/* Kai Shen <kaishen@linux.alibaba.com> */ 562306a36Sopenharmony_ci/* Copyright (c) 2020-2021, Alibaba Group */ 662306a36Sopenharmony_ci/* Authors: Bernard Metzler <bmt@zurich.ibm.com> */ 762306a36Sopenharmony_ci/* Copyright (c) 2008-2019, IBM Corporation */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "erdma_cm.h" 1062306a36Sopenharmony_ci#include "erdma_verbs.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_civoid erdma_qp_llp_close(struct erdma_qp *qp) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci struct erdma_qp_attrs qp_attrs; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci down_write(&qp->state_lock); 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci switch (qp->attrs.state) { 1962306a36Sopenharmony_ci case ERDMA_QP_STATE_RTS: 2062306a36Sopenharmony_ci case ERDMA_QP_STATE_RTR: 2162306a36Sopenharmony_ci case ERDMA_QP_STATE_IDLE: 2262306a36Sopenharmony_ci case ERDMA_QP_STATE_TERMINATE: 2362306a36Sopenharmony_ci qp_attrs.state = ERDMA_QP_STATE_CLOSING; 2462306a36Sopenharmony_ci erdma_modify_qp_internal(qp, &qp_attrs, ERDMA_QP_ATTR_STATE); 2562306a36Sopenharmony_ci break; 2662306a36Sopenharmony_ci case ERDMA_QP_STATE_CLOSING: 2762306a36Sopenharmony_ci qp->attrs.state = ERDMA_QP_STATE_IDLE; 2862306a36Sopenharmony_ci break; 2962306a36Sopenharmony_ci default: 3062306a36Sopenharmony_ci break; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (qp->cep) { 3462306a36Sopenharmony_ci erdma_cep_put(qp->cep); 3562306a36Sopenharmony_ci qp->cep = NULL; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci up_write(&qp->state_lock); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct ib_qp *erdma_get_ibqp(struct ib_device *ibdev, int id) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct erdma_qp *qp = find_qp_by_qpn(to_edev(ibdev), id); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (qp) 4662306a36Sopenharmony_ci return &qp->ibqp; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return NULL; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int erdma_modify_qp_state_to_rts(struct erdma_qp *qp, 5262306a36Sopenharmony_ci struct erdma_qp_attrs *attrs, 5362306a36Sopenharmony_ci enum erdma_qp_attr_mask mask) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci int ret; 5662306a36Sopenharmony_ci struct erdma_dev *dev = qp->dev; 5762306a36Sopenharmony_ci struct erdma_cmdq_modify_qp_req req; 5862306a36Sopenharmony_ci struct tcp_sock *tp; 5962306a36Sopenharmony_ci struct erdma_cep *cep = qp->cep; 6062306a36Sopenharmony_ci struct sockaddr_storage local_addr, remote_addr; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (!(mask & ERDMA_QP_ATTR_LLP_HANDLE)) 6362306a36Sopenharmony_ci return -EINVAL; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!(mask & ERDMA_QP_ATTR_MPA)) 6662306a36Sopenharmony_ci return -EINVAL; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ret = getname_local(cep->sock, &local_addr); 6962306a36Sopenharmony_ci if (ret < 0) 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ret = getname_peer(cep->sock, &remote_addr); 7362306a36Sopenharmony_ci if (ret < 0) 7462306a36Sopenharmony_ci return ret; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci qp->attrs.state = ERDMA_QP_STATE_RTS; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci tp = tcp_sk(qp->cep->sock->sk); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, 8162306a36Sopenharmony_ci CMDQ_OPCODE_MODIFY_QP); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci req.cfg = FIELD_PREP(ERDMA_CMD_MODIFY_QP_STATE_MASK, qp->attrs.state) | 8462306a36Sopenharmony_ci FIELD_PREP(ERDMA_CMD_MODIFY_QP_CC_MASK, qp->attrs.cc) | 8562306a36Sopenharmony_ci FIELD_PREP(ERDMA_CMD_MODIFY_QP_QPN_MASK, QP_ID(qp)); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci req.cookie = be32_to_cpu(qp->cep->mpa.ext_data.cookie); 8862306a36Sopenharmony_ci req.dip = to_sockaddr_in(remote_addr).sin_addr.s_addr; 8962306a36Sopenharmony_ci req.sip = to_sockaddr_in(local_addr).sin_addr.s_addr; 9062306a36Sopenharmony_ci req.dport = to_sockaddr_in(remote_addr).sin_port; 9162306a36Sopenharmony_ci req.sport = to_sockaddr_in(local_addr).sin_port; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci req.send_nxt = tp->snd_nxt; 9462306a36Sopenharmony_ci /* rsvd tcp seq for mpa-rsp in server. */ 9562306a36Sopenharmony_ci if (qp->attrs.qp_type == ERDMA_QP_PASSIVE) 9662306a36Sopenharmony_ci req.send_nxt += MPA_DEFAULT_HDR_LEN + qp->attrs.pd_len; 9762306a36Sopenharmony_ci req.recv_nxt = tp->rcv_nxt; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return erdma_post_cmd_wait(&dev->cmdq, &req, sizeof(req), NULL, NULL); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int erdma_modify_qp_state_to_stop(struct erdma_qp *qp, 10362306a36Sopenharmony_ci struct erdma_qp_attrs *attrs, 10462306a36Sopenharmony_ci enum erdma_qp_attr_mask mask) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct erdma_dev *dev = qp->dev; 10762306a36Sopenharmony_ci struct erdma_cmdq_modify_qp_req req; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci qp->attrs.state = attrs->state; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, 11262306a36Sopenharmony_ci CMDQ_OPCODE_MODIFY_QP); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci req.cfg = FIELD_PREP(ERDMA_CMD_MODIFY_QP_STATE_MASK, attrs->state) | 11562306a36Sopenharmony_ci FIELD_PREP(ERDMA_CMD_MODIFY_QP_QPN_MASK, QP_ID(qp)); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return erdma_post_cmd_wait(&dev->cmdq, &req, sizeof(req), NULL, NULL); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciint erdma_modify_qp_internal(struct erdma_qp *qp, struct erdma_qp_attrs *attrs, 12162306a36Sopenharmony_ci enum erdma_qp_attr_mask mask) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci bool need_reflush = false; 12462306a36Sopenharmony_ci int drop_conn, ret = 0; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!mask) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!(mask & ERDMA_QP_ATTR_STATE)) 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci switch (qp->attrs.state) { 13362306a36Sopenharmony_ci case ERDMA_QP_STATE_IDLE: 13462306a36Sopenharmony_ci case ERDMA_QP_STATE_RTR: 13562306a36Sopenharmony_ci if (attrs->state == ERDMA_QP_STATE_RTS) { 13662306a36Sopenharmony_ci ret = erdma_modify_qp_state_to_rts(qp, attrs, mask); 13762306a36Sopenharmony_ci } else if (attrs->state == ERDMA_QP_STATE_ERROR) { 13862306a36Sopenharmony_ci qp->attrs.state = ERDMA_QP_STATE_ERROR; 13962306a36Sopenharmony_ci need_reflush = true; 14062306a36Sopenharmony_ci if (qp->cep) { 14162306a36Sopenharmony_ci erdma_cep_put(qp->cep); 14262306a36Sopenharmony_ci qp->cep = NULL; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci ret = erdma_modify_qp_state_to_stop(qp, attrs, mask); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci case ERDMA_QP_STATE_RTS: 14862306a36Sopenharmony_ci drop_conn = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (attrs->state == ERDMA_QP_STATE_CLOSING || 15162306a36Sopenharmony_ci attrs->state == ERDMA_QP_STATE_TERMINATE || 15262306a36Sopenharmony_ci attrs->state == ERDMA_QP_STATE_ERROR) { 15362306a36Sopenharmony_ci ret = erdma_modify_qp_state_to_stop(qp, attrs, mask); 15462306a36Sopenharmony_ci drop_conn = 1; 15562306a36Sopenharmony_ci need_reflush = true; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (drop_conn) 15962306a36Sopenharmony_ci erdma_qp_cm_drop(qp); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci case ERDMA_QP_STATE_TERMINATE: 16362306a36Sopenharmony_ci if (attrs->state == ERDMA_QP_STATE_ERROR) 16462306a36Sopenharmony_ci qp->attrs.state = ERDMA_QP_STATE_ERROR; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci case ERDMA_QP_STATE_CLOSING: 16762306a36Sopenharmony_ci if (attrs->state == ERDMA_QP_STATE_IDLE) { 16862306a36Sopenharmony_ci qp->attrs.state = ERDMA_QP_STATE_IDLE; 16962306a36Sopenharmony_ci } else if (attrs->state == ERDMA_QP_STATE_ERROR) { 17062306a36Sopenharmony_ci ret = erdma_modify_qp_state_to_stop(qp, attrs, mask); 17162306a36Sopenharmony_ci qp->attrs.state = ERDMA_QP_STATE_ERROR; 17262306a36Sopenharmony_ci } else if (attrs->state != ERDMA_QP_STATE_CLOSING) { 17362306a36Sopenharmony_ci return -ECONNABORTED; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci default: 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (need_reflush && !ret && rdma_is_kernel_res(&qp->ibqp.res)) { 18162306a36Sopenharmony_ci qp->flags |= ERDMA_QP_IN_FLUSHING; 18262306a36Sopenharmony_ci mod_delayed_work(qp->dev->reflush_wq, &qp->reflush_dwork, 18362306a36Sopenharmony_ci usecs_to_jiffies(100)); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return ret; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void erdma_qp_safe_free(struct kref *ref) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct erdma_qp *qp = container_of(ref, struct erdma_qp, ref); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci complete(&qp->safe_free); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_civoid erdma_qp_put(struct erdma_qp *qp) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci WARN_ON(kref_read(&qp->ref) < 1); 19962306a36Sopenharmony_ci kref_put(&qp->ref, erdma_qp_safe_free); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_civoid erdma_qp_get(struct erdma_qp *qp) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci kref_get(&qp->ref); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int fill_inline_data(struct erdma_qp *qp, 20862306a36Sopenharmony_ci const struct ib_send_wr *send_wr, u16 wqe_idx, 20962306a36Sopenharmony_ci u32 sgl_offset, __le32 *length_field) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci u32 remain_size, copy_size, data_off, bytes = 0; 21262306a36Sopenharmony_ci char *data; 21362306a36Sopenharmony_ci int i = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci wqe_idx += (sgl_offset >> SQEBB_SHIFT); 21662306a36Sopenharmony_ci sgl_offset &= (SQEBB_SIZE - 1); 21762306a36Sopenharmony_ci data = get_queue_entry(qp->kern_qp.sq_buf, wqe_idx, qp->attrs.sq_size, 21862306a36Sopenharmony_ci SQEBB_SHIFT); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci while (i < send_wr->num_sge) { 22162306a36Sopenharmony_ci bytes += send_wr->sg_list[i].length; 22262306a36Sopenharmony_ci if (bytes > (int)ERDMA_MAX_INLINE) 22362306a36Sopenharmony_ci return -EINVAL; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci remain_size = send_wr->sg_list[i].length; 22662306a36Sopenharmony_ci data_off = 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci while (1) { 22962306a36Sopenharmony_ci copy_size = min(remain_size, SQEBB_SIZE - sgl_offset); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci memcpy(data + sgl_offset, 23262306a36Sopenharmony_ci (void *)(uintptr_t)send_wr->sg_list[i].addr + 23362306a36Sopenharmony_ci data_off, 23462306a36Sopenharmony_ci copy_size); 23562306a36Sopenharmony_ci remain_size -= copy_size; 23662306a36Sopenharmony_ci data_off += copy_size; 23762306a36Sopenharmony_ci sgl_offset += copy_size; 23862306a36Sopenharmony_ci wqe_idx += (sgl_offset >> SQEBB_SHIFT); 23962306a36Sopenharmony_ci sgl_offset &= (SQEBB_SIZE - 1); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci data = get_queue_entry(qp->kern_qp.sq_buf, wqe_idx, 24262306a36Sopenharmony_ci qp->attrs.sq_size, SQEBB_SHIFT); 24362306a36Sopenharmony_ci if (!remain_size) 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci i++; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci *length_field = cpu_to_le32(bytes); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return bytes; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int fill_sgl(struct erdma_qp *qp, const struct ib_send_wr *send_wr, 25562306a36Sopenharmony_ci u16 wqe_idx, u32 sgl_offset, __le32 *length_field) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci int i = 0; 25862306a36Sopenharmony_ci u32 bytes = 0; 25962306a36Sopenharmony_ci char *sgl; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (send_wr->num_sge > qp->dev->attrs.max_send_sge) 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (sgl_offset & 0xF) 26562306a36Sopenharmony_ci return -EINVAL; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci while (i < send_wr->num_sge) { 26862306a36Sopenharmony_ci wqe_idx += (sgl_offset >> SQEBB_SHIFT); 26962306a36Sopenharmony_ci sgl_offset &= (SQEBB_SIZE - 1); 27062306a36Sopenharmony_ci sgl = get_queue_entry(qp->kern_qp.sq_buf, wqe_idx, 27162306a36Sopenharmony_ci qp->attrs.sq_size, SQEBB_SHIFT); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci bytes += send_wr->sg_list[i].length; 27462306a36Sopenharmony_ci memcpy(sgl + sgl_offset, &send_wr->sg_list[i], 27562306a36Sopenharmony_ci sizeof(struct ib_sge)); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci sgl_offset += sizeof(struct ib_sge); 27862306a36Sopenharmony_ci i++; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci *length_field = cpu_to_le32(bytes); 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int erdma_push_one_sqe(struct erdma_qp *qp, u16 *pi, 28662306a36Sopenharmony_ci const struct ib_send_wr *send_wr) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci u32 wqe_size, wqebb_cnt, hw_op, flags, sgl_offset; 28962306a36Sopenharmony_ci u32 idx = *pi & (qp->attrs.sq_size - 1); 29062306a36Sopenharmony_ci enum ib_wr_opcode op = send_wr->opcode; 29162306a36Sopenharmony_ci struct erdma_atomic_sqe *atomic_sqe; 29262306a36Sopenharmony_ci struct erdma_readreq_sqe *read_sqe; 29362306a36Sopenharmony_ci struct erdma_reg_mr_sqe *regmr_sge; 29462306a36Sopenharmony_ci struct erdma_write_sqe *write_sqe; 29562306a36Sopenharmony_ci struct erdma_send_sqe *send_sqe; 29662306a36Sopenharmony_ci struct ib_rdma_wr *rdma_wr; 29762306a36Sopenharmony_ci struct erdma_sge *sge; 29862306a36Sopenharmony_ci __le32 *length_field; 29962306a36Sopenharmony_ci struct erdma_mr *mr; 30062306a36Sopenharmony_ci u64 wqe_hdr, *entry; 30162306a36Sopenharmony_ci u32 attrs; 30262306a36Sopenharmony_ci int ret; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci entry = get_queue_entry(qp->kern_qp.sq_buf, idx, qp->attrs.sq_size, 30562306a36Sopenharmony_ci SQEBB_SHIFT); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Clear the SQE header section. */ 30862306a36Sopenharmony_ci *entry = 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci qp->kern_qp.swr_tbl[idx] = send_wr->wr_id; 31162306a36Sopenharmony_ci flags = send_wr->send_flags; 31262306a36Sopenharmony_ci wqe_hdr = FIELD_PREP( 31362306a36Sopenharmony_ci ERDMA_SQE_HDR_CE_MASK, 31462306a36Sopenharmony_ci ((flags & IB_SEND_SIGNALED) || qp->kern_qp.sig_all) ? 1 : 0); 31562306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_SE_MASK, 31662306a36Sopenharmony_ci flags & IB_SEND_SOLICITED ? 1 : 0); 31762306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_FENCE_MASK, 31862306a36Sopenharmony_ci flags & IB_SEND_FENCE ? 1 : 0); 31962306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_INLINE_MASK, 32062306a36Sopenharmony_ci flags & IB_SEND_INLINE ? 1 : 0); 32162306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_QPN_MASK, QP_ID(qp)); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci switch (op) { 32462306a36Sopenharmony_ci case IB_WR_RDMA_WRITE: 32562306a36Sopenharmony_ci case IB_WR_RDMA_WRITE_WITH_IMM: 32662306a36Sopenharmony_ci hw_op = ERDMA_OP_WRITE; 32762306a36Sopenharmony_ci if (op == IB_WR_RDMA_WRITE_WITH_IMM) 32862306a36Sopenharmony_ci hw_op = ERDMA_OP_WRITE_WITH_IMM; 32962306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, hw_op); 33062306a36Sopenharmony_ci rdma_wr = container_of(send_wr, struct ib_rdma_wr, wr); 33162306a36Sopenharmony_ci write_sqe = (struct erdma_write_sqe *)entry; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci write_sqe->imm_data = send_wr->ex.imm_data; 33462306a36Sopenharmony_ci write_sqe->sink_stag = cpu_to_le32(rdma_wr->rkey); 33562306a36Sopenharmony_ci write_sqe->sink_to_h = 33662306a36Sopenharmony_ci cpu_to_le32(upper_32_bits(rdma_wr->remote_addr)); 33762306a36Sopenharmony_ci write_sqe->sink_to_l = 33862306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(rdma_wr->remote_addr)); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci length_field = &write_sqe->length; 34162306a36Sopenharmony_ci wqe_size = sizeof(struct erdma_write_sqe); 34262306a36Sopenharmony_ci sgl_offset = wqe_size; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case IB_WR_RDMA_READ: 34562306a36Sopenharmony_ci case IB_WR_RDMA_READ_WITH_INV: 34662306a36Sopenharmony_ci read_sqe = (struct erdma_readreq_sqe *)entry; 34762306a36Sopenharmony_ci if (unlikely(send_wr->num_sge != 1)) 34862306a36Sopenharmony_ci return -EINVAL; 34962306a36Sopenharmony_ci hw_op = ERDMA_OP_READ; 35062306a36Sopenharmony_ci if (op == IB_WR_RDMA_READ_WITH_INV) { 35162306a36Sopenharmony_ci hw_op = ERDMA_OP_READ_WITH_INV; 35262306a36Sopenharmony_ci read_sqe->invalid_stag = 35362306a36Sopenharmony_ci cpu_to_le32(send_wr->ex.invalidate_rkey); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, hw_op); 35762306a36Sopenharmony_ci rdma_wr = container_of(send_wr, struct ib_rdma_wr, wr); 35862306a36Sopenharmony_ci read_sqe->length = cpu_to_le32(send_wr->sg_list[0].length); 35962306a36Sopenharmony_ci read_sqe->sink_stag = cpu_to_le32(send_wr->sg_list[0].lkey); 36062306a36Sopenharmony_ci read_sqe->sink_to_l = 36162306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(send_wr->sg_list[0].addr)); 36262306a36Sopenharmony_ci read_sqe->sink_to_h = 36362306a36Sopenharmony_ci cpu_to_le32(upper_32_bits(send_wr->sg_list[0].addr)); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci sge = get_queue_entry(qp->kern_qp.sq_buf, idx + 1, 36662306a36Sopenharmony_ci qp->attrs.sq_size, SQEBB_SHIFT); 36762306a36Sopenharmony_ci sge->addr = cpu_to_le64(rdma_wr->remote_addr); 36862306a36Sopenharmony_ci sge->key = cpu_to_le32(rdma_wr->rkey); 36962306a36Sopenharmony_ci sge->length = cpu_to_le32(send_wr->sg_list[0].length); 37062306a36Sopenharmony_ci wqe_size = sizeof(struct erdma_readreq_sqe) + 37162306a36Sopenharmony_ci send_wr->num_sge * sizeof(struct ib_sge); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci goto out; 37462306a36Sopenharmony_ci case IB_WR_SEND: 37562306a36Sopenharmony_ci case IB_WR_SEND_WITH_IMM: 37662306a36Sopenharmony_ci case IB_WR_SEND_WITH_INV: 37762306a36Sopenharmony_ci send_sqe = (struct erdma_send_sqe *)entry; 37862306a36Sopenharmony_ci hw_op = ERDMA_OP_SEND; 37962306a36Sopenharmony_ci if (op == IB_WR_SEND_WITH_IMM) { 38062306a36Sopenharmony_ci hw_op = ERDMA_OP_SEND_WITH_IMM; 38162306a36Sopenharmony_ci send_sqe->imm_data = send_wr->ex.imm_data; 38262306a36Sopenharmony_ci } else if (op == IB_WR_SEND_WITH_INV) { 38362306a36Sopenharmony_ci hw_op = ERDMA_OP_SEND_WITH_INV; 38462306a36Sopenharmony_ci send_sqe->invalid_stag = 38562306a36Sopenharmony_ci cpu_to_le32(send_wr->ex.invalidate_rkey); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, hw_op); 38862306a36Sopenharmony_ci length_field = &send_sqe->length; 38962306a36Sopenharmony_ci wqe_size = sizeof(struct erdma_send_sqe); 39062306a36Sopenharmony_ci sgl_offset = wqe_size; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci case IB_WR_REG_MR: 39462306a36Sopenharmony_ci wqe_hdr |= 39562306a36Sopenharmony_ci FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, ERDMA_OP_REG_MR); 39662306a36Sopenharmony_ci regmr_sge = (struct erdma_reg_mr_sqe *)entry; 39762306a36Sopenharmony_ci mr = to_emr(reg_wr(send_wr)->mr); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci mr->access = ERDMA_MR_ACC_LR | 40062306a36Sopenharmony_ci to_erdma_access_flags(reg_wr(send_wr)->access); 40162306a36Sopenharmony_ci regmr_sge->addr = cpu_to_le64(mr->ibmr.iova); 40262306a36Sopenharmony_ci regmr_sge->length = cpu_to_le32(mr->ibmr.length); 40362306a36Sopenharmony_ci regmr_sge->stag = cpu_to_le32(reg_wr(send_wr)->key); 40462306a36Sopenharmony_ci attrs = FIELD_PREP(ERDMA_SQE_MR_ACCESS_MASK, mr->access) | 40562306a36Sopenharmony_ci FIELD_PREP(ERDMA_SQE_MR_MTT_CNT_MASK, 40662306a36Sopenharmony_ci mr->mem.mtt_nents); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (mr->mem.mtt_nents <= ERDMA_MAX_INLINE_MTT_ENTRIES) { 40962306a36Sopenharmony_ci attrs |= FIELD_PREP(ERDMA_SQE_MR_MTT_TYPE_MASK, 0); 41062306a36Sopenharmony_ci /* Copy SGLs to SQE content to accelerate */ 41162306a36Sopenharmony_ci memcpy(get_queue_entry(qp->kern_qp.sq_buf, idx + 1, 41262306a36Sopenharmony_ci qp->attrs.sq_size, SQEBB_SHIFT), 41362306a36Sopenharmony_ci mr->mem.mtt->buf, MTT_SIZE(mr->mem.mtt_nents)); 41462306a36Sopenharmony_ci wqe_size = sizeof(struct erdma_reg_mr_sqe) + 41562306a36Sopenharmony_ci MTT_SIZE(mr->mem.mtt_nents); 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci attrs |= FIELD_PREP(ERDMA_SQE_MR_MTT_TYPE_MASK, 1); 41862306a36Sopenharmony_ci wqe_size = sizeof(struct erdma_reg_mr_sqe); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci regmr_sge->attrs = cpu_to_le32(attrs); 42262306a36Sopenharmony_ci goto out; 42362306a36Sopenharmony_ci case IB_WR_LOCAL_INV: 42462306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, 42562306a36Sopenharmony_ci ERDMA_OP_LOCAL_INV); 42662306a36Sopenharmony_ci regmr_sge = (struct erdma_reg_mr_sqe *)entry; 42762306a36Sopenharmony_ci regmr_sge->stag = cpu_to_le32(send_wr->ex.invalidate_rkey); 42862306a36Sopenharmony_ci wqe_size = sizeof(struct erdma_reg_mr_sqe); 42962306a36Sopenharmony_ci goto out; 43062306a36Sopenharmony_ci case IB_WR_ATOMIC_CMP_AND_SWP: 43162306a36Sopenharmony_ci case IB_WR_ATOMIC_FETCH_AND_ADD: 43262306a36Sopenharmony_ci atomic_sqe = (struct erdma_atomic_sqe *)entry; 43362306a36Sopenharmony_ci if (op == IB_WR_ATOMIC_CMP_AND_SWP) { 43462306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, 43562306a36Sopenharmony_ci ERDMA_OP_ATOMIC_CAS); 43662306a36Sopenharmony_ci atomic_sqe->fetchadd_swap_data = 43762306a36Sopenharmony_ci cpu_to_le64(atomic_wr(send_wr)->swap); 43862306a36Sopenharmony_ci atomic_sqe->cmp_data = 43962306a36Sopenharmony_ci cpu_to_le64(atomic_wr(send_wr)->compare_add); 44062306a36Sopenharmony_ci } else { 44162306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, 44262306a36Sopenharmony_ci ERDMA_OP_ATOMIC_FAA); 44362306a36Sopenharmony_ci atomic_sqe->fetchadd_swap_data = 44462306a36Sopenharmony_ci cpu_to_le64(atomic_wr(send_wr)->compare_add); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci sge = get_queue_entry(qp->kern_qp.sq_buf, idx + 1, 44862306a36Sopenharmony_ci qp->attrs.sq_size, SQEBB_SHIFT); 44962306a36Sopenharmony_ci sge->addr = cpu_to_le64(atomic_wr(send_wr)->remote_addr); 45062306a36Sopenharmony_ci sge->key = cpu_to_le32(atomic_wr(send_wr)->rkey); 45162306a36Sopenharmony_ci sge++; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci sge->addr = cpu_to_le64(send_wr->sg_list[0].addr); 45462306a36Sopenharmony_ci sge->key = cpu_to_le32(send_wr->sg_list[0].lkey); 45562306a36Sopenharmony_ci sge->length = cpu_to_le32(send_wr->sg_list[0].length); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci wqe_size = sizeof(*atomic_sqe); 45862306a36Sopenharmony_ci goto out; 45962306a36Sopenharmony_ci default: 46062306a36Sopenharmony_ci return -EOPNOTSUPP; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (flags & IB_SEND_INLINE) { 46462306a36Sopenharmony_ci ret = fill_inline_data(qp, send_wr, idx, sgl_offset, 46562306a36Sopenharmony_ci length_field); 46662306a36Sopenharmony_ci if (ret < 0) 46762306a36Sopenharmony_ci return -EINVAL; 46862306a36Sopenharmony_ci wqe_size += ret; 46962306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_SGL_LEN_MASK, ret); 47062306a36Sopenharmony_ci } else { 47162306a36Sopenharmony_ci ret = fill_sgl(qp, send_wr, idx, sgl_offset, length_field); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci return -EINVAL; 47462306a36Sopenharmony_ci wqe_size += send_wr->num_sge * sizeof(struct ib_sge); 47562306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_SGL_LEN_MASK, 47662306a36Sopenharmony_ci send_wr->num_sge); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ciout: 48062306a36Sopenharmony_ci wqebb_cnt = SQEBB_COUNT(wqe_size); 48162306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_WQEBB_CNT_MASK, wqebb_cnt - 1); 48262306a36Sopenharmony_ci *pi += wqebb_cnt; 48362306a36Sopenharmony_ci wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_WQEBB_INDEX_MASK, *pi); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci *entry = wqe_hdr; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void kick_sq_db(struct erdma_qp *qp, u16 pi) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci u64 db_data = FIELD_PREP(ERDMA_SQE_HDR_QPN_MASK, QP_ID(qp)) | 49362306a36Sopenharmony_ci FIELD_PREP(ERDMA_SQE_HDR_WQEBB_INDEX_MASK, pi); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci *(u64 *)qp->kern_qp.sq_db_info = db_data; 49662306a36Sopenharmony_ci writeq(db_data, qp->kern_qp.hw_sq_db); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ciint erdma_post_send(struct ib_qp *ibqp, const struct ib_send_wr *send_wr, 50062306a36Sopenharmony_ci const struct ib_send_wr **bad_send_wr) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct erdma_qp *qp = to_eqp(ibqp); 50362306a36Sopenharmony_ci int ret = 0; 50462306a36Sopenharmony_ci const struct ib_send_wr *wr = send_wr; 50562306a36Sopenharmony_ci unsigned long flags; 50662306a36Sopenharmony_ci u16 sq_pi; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!send_wr) 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci spin_lock_irqsave(&qp->lock, flags); 51262306a36Sopenharmony_ci sq_pi = qp->kern_qp.sq_pi; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci while (wr) { 51562306a36Sopenharmony_ci if ((u16)(sq_pi - qp->kern_qp.sq_ci) >= qp->attrs.sq_size) { 51662306a36Sopenharmony_ci ret = -ENOMEM; 51762306a36Sopenharmony_ci *bad_send_wr = send_wr; 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci ret = erdma_push_one_sqe(qp, &sq_pi, wr); 52262306a36Sopenharmony_ci if (ret) { 52362306a36Sopenharmony_ci *bad_send_wr = wr; 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci qp->kern_qp.sq_pi = sq_pi; 52762306a36Sopenharmony_ci kick_sq_db(qp, sq_pi); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci wr = wr->next; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci spin_unlock_irqrestore(&qp->lock, flags); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (unlikely(qp->flags & ERDMA_QP_IN_FLUSHING)) 53462306a36Sopenharmony_ci mod_delayed_work(qp->dev->reflush_wq, &qp->reflush_dwork, 53562306a36Sopenharmony_ci usecs_to_jiffies(100)); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return ret; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic int erdma_post_recv_one(struct erdma_qp *qp, 54162306a36Sopenharmony_ci const struct ib_recv_wr *recv_wr) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct erdma_rqe *rqe = 54462306a36Sopenharmony_ci get_queue_entry(qp->kern_qp.rq_buf, qp->kern_qp.rq_pi, 54562306a36Sopenharmony_ci qp->attrs.rq_size, RQE_SHIFT); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci rqe->qe_idx = cpu_to_le16(qp->kern_qp.rq_pi + 1); 54862306a36Sopenharmony_ci rqe->qpn = cpu_to_le32(QP_ID(qp)); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (recv_wr->num_sge == 0) { 55162306a36Sopenharmony_ci rqe->length = 0; 55262306a36Sopenharmony_ci } else if (recv_wr->num_sge == 1) { 55362306a36Sopenharmony_ci rqe->stag = cpu_to_le32(recv_wr->sg_list[0].lkey); 55462306a36Sopenharmony_ci rqe->to = cpu_to_le64(recv_wr->sg_list[0].addr); 55562306a36Sopenharmony_ci rqe->length = cpu_to_le32(recv_wr->sg_list[0].length); 55662306a36Sopenharmony_ci } else { 55762306a36Sopenharmony_ci return -EINVAL; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci *(u64 *)qp->kern_qp.rq_db_info = *(u64 *)rqe; 56162306a36Sopenharmony_ci writeq(*(u64 *)rqe, qp->kern_qp.hw_rq_db); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci qp->kern_qp.rwr_tbl[qp->kern_qp.rq_pi & (qp->attrs.rq_size - 1)] = 56462306a36Sopenharmony_ci recv_wr->wr_id; 56562306a36Sopenharmony_ci qp->kern_qp.rq_pi++; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ciint erdma_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *recv_wr, 57162306a36Sopenharmony_ci const struct ib_recv_wr **bad_recv_wr) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci const struct ib_recv_wr *wr = recv_wr; 57462306a36Sopenharmony_ci struct erdma_qp *qp = to_eqp(ibqp); 57562306a36Sopenharmony_ci unsigned long flags; 57662306a36Sopenharmony_ci int ret; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci spin_lock_irqsave(&qp->lock, flags); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci while (wr) { 58162306a36Sopenharmony_ci ret = erdma_post_recv_one(qp, wr); 58262306a36Sopenharmony_ci if (ret) { 58362306a36Sopenharmony_ci *bad_recv_wr = wr; 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci wr = wr->next; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci spin_unlock_irqrestore(&qp->lock, flags); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (unlikely(qp->flags & ERDMA_QP_IN_FLUSHING)) 59262306a36Sopenharmony_ci mod_delayed_work(qp->dev->reflush_wq, &qp->reflush_dwork, 59362306a36Sopenharmony_ci usecs_to_jiffies(100)); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci} 597