// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause // Authors: Bernard Metzler // Copyright (c) 2008-2019, IBM Corporation #include #include #include #include #include #include #include #include #include #include #include "siw_abi.h" #include "siw.h" static void siw_free_context(struct ibv_context *ibv_ctx); static int siw_query_device(struct ibv_context *context, const struct ibv_query_device_ex_input *input, struct ibv_device_attr_ex *attr, size_t attr_size) { struct ib_uverbs_ex_query_device_resp resp; size_t resp_size = sizeof(resp); uint64_t raw_fw_ver; unsigned int major, minor, sub_minor; int rv; rv = ibv_cmd_query_device_any(context, input, attr, attr_size, &resp, &resp_size); if (rv) return rv; raw_fw_ver = resp.base.fw_ver; major = (raw_fw_ver >> 32) & 0xffff; minor = (raw_fw_ver >> 16) & 0xffff; sub_minor = raw_fw_ver & 0xffff; snprintf(attr->orig_attr.fw_ver, sizeof(attr->orig_attr.fw_ver), "%d.%d.%d", major, minor, sub_minor); return 0; } static int siw_query_port(struct ibv_context *ctx, uint8_t port, struct ibv_port_attr *attr) { struct ibv_query_port cmd; memset(&cmd, 0, sizeof(cmd)); return ibv_cmd_query_port(ctx, port, attr, &cmd, sizeof(cmd)); } static int siw_query_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mask, struct ibv_qp_init_attr *init_attr) { struct ibv_query_qp cmd; memset(&cmd, 0, sizeof(cmd)); return ibv_cmd_query_qp(qp, attr, attr_mask, init_attr, &cmd, sizeof(cmd)); } static struct ibv_pd *siw_alloc_pd(struct ibv_context *ctx) { struct ibv_alloc_pd cmd; struct ib_uverbs_alloc_pd_resp resp; struct ibv_pd *pd; memset(&cmd, 0, sizeof(cmd)); pd = calloc(1, sizeof(*pd)); if (!pd) return NULL; if (ibv_cmd_alloc_pd(ctx, pd, &cmd, sizeof(cmd), &resp, sizeof(resp))) { free(pd); return NULL; } return pd; } static int siw_free_pd(struct ibv_pd *pd) { int rv; rv = ibv_cmd_dealloc_pd(pd); if (rv) return rv; free(pd); return 0; } static struct ibv_mr *siw_reg_mr(struct ibv_pd *pd, void *addr, size_t len, uint64_t hca_va, int access) { struct siw_cmd_reg_mr cmd = {}; struct siw_cmd_reg_mr_resp resp = {}; struct siw_mr *mr; int rv; mr = calloc(1, sizeof(*mr)); if (!mr) return NULL; rv = ibv_cmd_reg_mr(pd, addr, len, hca_va, access, &mr->base_mr, &cmd.ibv_cmd, sizeof(cmd), &resp.ibv_resp, sizeof(resp)); if (rv) { free(mr); return NULL; } return &mr->base_mr.ibv_mr; } static int siw_dereg_mr(struct verbs_mr *base_mr) { struct siw_mr *mr = mr_base2siw(base_mr); int rv; rv = ibv_cmd_dereg_mr(base_mr); if (rv) return rv; free(mr); return 0; } static struct ibv_cq *siw_create_cq(struct ibv_context *ctx, int num_cqe, struct ibv_comp_channel *channel, int comp_vector) { struct siw_cmd_create_cq cmd = {}; struct siw_cmd_create_cq_resp resp = {}; struct siw_cq *cq; int cq_size, rv; cq = calloc(1, sizeof(*cq)); if (!cq) return NULL; rv = ibv_cmd_create_cq(ctx, num_cqe, channel, comp_vector, &cq->base_cq, &cmd.ibv_cmd, sizeof(cmd), &resp.ibv_resp, sizeof(resp)); if (rv) { verbs_err(verbs_get_ctx(ctx), "libsiw: CQ creation failed: %d\n", rv); free(cq); return NULL; } if (resp.cq_key == SIW_INVAL_UOBJ_KEY) { verbs_err(verbs_get_ctx(ctx), "libsiw: prepare CQ mapping failed\n"); goto fail; } pthread_spin_init(&cq->lock, PTHREAD_PROCESS_PRIVATE); cq->id = resp.cq_id; cq->num_cqe = resp.num_cqe; cq_size = resp.num_cqe * sizeof(struct siw_cqe) + sizeof(struct siw_cq_ctrl); cq->queue = mmap(NULL, cq_size, PROT_READ | PROT_WRITE, MAP_SHARED, ctx->cmd_fd, resp.cq_key); if (cq->queue == MAP_FAILED) { verbs_err(verbs_get_ctx(ctx), "libsiw: CQ mapping failed: %d", errno); goto fail; } cq->ctrl = (struct siw_cq_ctrl *)&cq->queue[cq->num_cqe]; cq->ctrl->flags = SIW_NOTIFY_NOT; return &cq->base_cq; fail: ibv_cmd_destroy_cq(&cq->base_cq); free(cq); return NULL; } static int siw_destroy_cq(struct ibv_cq *base_cq) { struct siw_cq *cq = cq_base2siw(base_cq); int rv; assert(pthread_spin_trylock(&cq->lock)); if (cq->queue) munmap(cq->queue, cq->num_cqe * sizeof(struct siw_cqe) + sizeof(struct siw_cq_ctrl)); rv = ibv_cmd_destroy_cq(base_cq); if (rv) { pthread_spin_unlock(&cq->lock); return rv; } pthread_spin_destroy(&cq->lock); free(cq); return 0; } static struct ibv_srq *siw_create_srq(struct ibv_pd *pd, struct ibv_srq_init_attr *attr) { struct siw_cmd_create_srq cmd = {}; struct siw_cmd_create_srq_resp resp = {}; struct ibv_context *ctx = pd->context; struct siw_srq *srq; int rv, rq_size; srq = calloc(1, sizeof(*srq)); if (!srq) return NULL; rv = ibv_cmd_create_srq(pd, &srq->base_srq, attr, &cmd.ibv_cmd, sizeof(cmd), &resp.ibv_resp, sizeof(resp)); if (rv) { verbs_err(verbs_get_ctx(pd->context), "libsiw: creating SRQ failed\n"); free(srq); return NULL; } if (resp.srq_key == SIW_INVAL_UOBJ_KEY) { verbs_err(verbs_get_ctx(pd->context), "libsiw: prepare SRQ mapping failed\n"); goto fail; } pthread_spin_init(&srq->lock, PTHREAD_PROCESS_PRIVATE); rq_size = resp.num_rqe * sizeof(struct siw_rqe); srq->num_rqe = resp.num_rqe; srq->recvq = mmap(NULL, rq_size, PROT_READ | PROT_WRITE, MAP_SHARED, ctx->cmd_fd, resp.srq_key); if (srq->recvq == MAP_FAILED) { verbs_err(verbs_get_ctx(pd->context), "libsiw: SRQ mapping failed: %d", errno); goto fail; } return &srq->base_srq; fail: ibv_cmd_destroy_srq(&srq->base_srq); free(srq); return NULL; } static int siw_modify_srq(struct ibv_srq *base_srq, struct ibv_srq_attr *attr, int attr_mask) { struct ibv_modify_srq cmd = {}; struct siw_srq *srq = srq_base2siw(base_srq); int rv; pthread_spin_lock(&srq->lock); rv = ibv_cmd_modify_srq(base_srq, attr, attr_mask, &cmd, sizeof(cmd)); pthread_spin_unlock(&srq->lock); return rv; } static int siw_destroy_srq(struct ibv_srq *base_srq) { struct siw_srq *srq = srq_base2siw(base_srq); int rv; assert(pthread_spin_trylock(&srq->lock)); rv = ibv_cmd_destroy_srq(base_srq); if (rv) { pthread_spin_unlock(&srq->lock); return rv; } if (srq->recvq) munmap(srq->recvq, srq->num_rqe * sizeof(struct siw_rqe)); pthread_spin_destroy(&srq->lock); free(srq); return 0; } static struct ibv_qp *siw_create_qp(struct ibv_pd *pd, struct ibv_qp_init_attr *attr) { struct siw_cmd_create_qp cmd = {}; struct siw_cmd_create_qp_resp resp = {}; struct siw_qp *qp; struct ibv_context *base_ctx = pd->context; int sq_size, rq_size, rv; memset(&cmd, 0, sizeof(cmd)); memset(&resp, 0, sizeof(resp)); qp = calloc(1, sizeof(*qp)); if (!qp) return NULL; rv = ibv_cmd_create_qp(pd, &qp->base_qp, attr, &cmd.ibv_cmd, sizeof(cmd), &resp.ibv_resp, sizeof(resp)); if (rv) { verbs_err(verbs_get_ctx(pd->context), "libsiw: QP creation failed\n"); free(qp); return NULL; } if (resp.sq_key == SIW_INVAL_UOBJ_KEY || resp.rq_key == SIW_INVAL_UOBJ_KEY) { verbs_err(verbs_get_ctx(pd->context), "libsiw: prepare QP mapping failed\n"); goto fail; } qp->id = resp.qp_id; qp->num_sqe = resp.num_sqe; qp->num_rqe = resp.num_rqe; qp->sq_sig_all = attr->sq_sig_all; /* Init doorbell request structure */ qp->db_req.hdr.command = IB_USER_VERBS_CMD_POST_SEND; qp->db_req.hdr.in_words = sizeof(qp->db_req) / 4; qp->db_req.hdr.out_words = sizeof(qp->db_resp) / 4; qp->db_req.response = (uintptr_t)&qp->db_resp; qp->db_req.wr_count = 0; qp->db_req.sge_count = 0; qp->db_req.wqe_size = sizeof(struct ibv_send_wr); pthread_spin_init(&qp->sq_lock, PTHREAD_PROCESS_PRIVATE); pthread_spin_init(&qp->rq_lock, PTHREAD_PROCESS_PRIVATE); sq_size = resp.num_sqe * sizeof(struct siw_sqe); qp->sendq = mmap(NULL, sq_size, PROT_READ | PROT_WRITE, MAP_SHARED, base_ctx->cmd_fd, resp.sq_key); if (qp->sendq == MAP_FAILED) { verbs_err(verbs_get_ctx(pd->context), "libsiw: SQ mapping failed: %d", errno); qp->sendq = NULL; goto fail; } if (attr->srq) { qp->srq = srq_base2siw(attr->srq); } else { rq_size = resp.num_rqe * sizeof(struct siw_rqe); qp->recvq = mmap(NULL, rq_size, PROT_READ | PROT_WRITE, MAP_SHARED, base_ctx->cmd_fd, resp.rq_key); if (qp->recvq == MAP_FAILED) { verbs_err(verbs_get_ctx(pd->context), "libsiw: RQ mapping failed: %d\n", resp.num_rqe); qp->recvq = NULL; goto fail; } } qp->db_req.qp_handle = qp->base_qp.handle; return &qp->base_qp; fail: ibv_cmd_destroy_qp(&qp->base_qp); if (qp->sendq) munmap(qp->sendq, qp->num_sqe * sizeof(struct siw_sqe)); if (qp->recvq) munmap(qp->recvq, qp->num_rqe * sizeof(struct siw_rqe)); free(qp); return NULL; } static int siw_modify_qp(struct ibv_qp *base_qp, struct ibv_qp_attr *attr, int attr_mask) { struct ibv_modify_qp cmd; struct siw_qp *qp = qp_base2siw(base_qp); int rv; memset(&cmd, 0, sizeof(cmd)); pthread_spin_lock(&qp->sq_lock); pthread_spin_lock(&qp->rq_lock); rv = ibv_cmd_modify_qp(base_qp, attr, attr_mask, &cmd, sizeof(cmd)); pthread_spin_unlock(&qp->rq_lock); pthread_spin_unlock(&qp->sq_lock); return rv; } static int siw_destroy_qp(struct ibv_qp *base_qp) { struct siw_qp *qp = qp_base2siw(base_qp); int rv; assert(pthread_spin_trylock(&qp->sq_lock)); assert(pthread_spin_trylock(&qp->rq_lock)); if (qp->sendq) munmap(qp->sendq, qp->num_sqe * sizeof(struct siw_sqe)); if (qp->recvq) munmap(qp->recvq, qp->num_rqe * sizeof(struct siw_rqe)); rv = ibv_cmd_destroy_qp(base_qp); if (rv) { pthread_spin_unlock(&qp->rq_lock); pthread_spin_unlock(&qp->sq_lock); return rv; } pthread_spin_destroy(&qp->rq_lock); pthread_spin_destroy(&qp->sq_lock); free(qp); return 0; } static void siw_async_event(struct ibv_context *ctx, struct ibv_async_event *event) { struct ibv_qp *base_qp = event->element.qp; struct ibv_cq *base_cq = event->element.cq; switch (event->event_type) { case IBV_EVENT_CQ_ERR: verbs_err(verbs_get_ctx(ctx), "libsiw: CQ[%d] event: error\n", cq_base2siw(base_cq)->id); break; case IBV_EVENT_QP_FATAL: verbs_err(verbs_get_ctx(ctx), "libsiw: QP[%d] event: fatal error\n", qp_base2siw(base_qp)->id); break; case IBV_EVENT_QP_REQ_ERR: verbs_err(verbs_get_ctx(ctx), "libsiw: QP[%d] event: request error\n", qp_base2siw(base_qp)->id); break; case IBV_EVENT_QP_ACCESS_ERR: verbs_err(verbs_get_ctx(ctx), "libsiw: QP[%d] event: access error\n", qp_base2siw(base_qp)->id); break; case IBV_EVENT_SQ_DRAINED: case IBV_EVENT_COMM_EST: case IBV_EVENT_QP_LAST_WQE_REACHED: break; default: break; } } static int siw_notify_cq(struct ibv_cq *ibcq, int solicited) { struct siw_cq *cq = cq_base2siw(ibcq); int rv = 0; if (solicited) atomic_store((_Atomic(uint32_t) *)&cq->ctrl->flags, SIW_NOTIFY_SOLICITED); else atomic_store((_Atomic(uint32_t) *)&cq->ctrl->flags, SIW_NOTIFY_SOLICITED | SIW_NOTIFY_NEXT_COMPLETION); return rv; } static const struct { enum ibv_wr_opcode base; enum siw_opcode siw; } map_send_opcode[IBV_WR_DRIVER1 + 1] = { { IBV_WR_RDMA_WRITE, SIW_OP_WRITE}, { IBV_WR_RDMA_WRITE_WITH_IMM, SIW_NUM_OPCODES + 1 }, { IBV_WR_SEND, SIW_OP_SEND }, { IBV_WR_SEND_WITH_IMM, SIW_NUM_OPCODES + 1 }, { IBV_WR_RDMA_READ, SIW_OP_READ }, { IBV_WR_ATOMIC_CMP_AND_SWP, SIW_NUM_OPCODES + 1 }, { IBV_WR_ATOMIC_FETCH_AND_ADD, SIW_NUM_OPCODES + 1 }, { IBV_WR_LOCAL_INV, SIW_NUM_OPCODES + 1 }, { IBV_WR_BIND_MW, SIW_NUM_OPCODES + 1 }, { IBV_WR_SEND_WITH_INV, SIW_OP_SEND_REMOTE_INV }, { IBV_WR_TSO, SIW_NUM_OPCODES + 1 }, { IBV_WR_DRIVER1, SIW_NUM_OPCODES + 1 } }; static inline uint16_t map_send_flags(int ibv_flags) { uint16_t flags = SIW_WQE_VALID; if (ibv_flags & IBV_SEND_SIGNALED) flags |= SIW_WQE_SIGNALLED; if (ibv_flags & IBV_SEND_SOLICITED) flags |= SIW_WQE_SOLICITED; if (ibv_flags & IBV_SEND_INLINE) flags |= SIW_WQE_INLINE; if (ibv_flags & IBV_SEND_FENCE) flags |= SIW_WQE_READ_FENCE; return flags; } static inline int push_send_wqe(struct ibv_qp *base_qp, struct ibv_send_wr *base_wr, struct siw_sqe *siw_sqe, int sig_all) { uint32_t flags = map_send_flags(base_wr->send_flags); atomic_ushort *fp = (atomic_ushort *)&siw_sqe->flags; siw_sqe->id = base_wr->wr_id; siw_sqe->num_sge = base_wr->num_sge; siw_sqe->raddr = base_wr->wr.rdma.remote_addr; siw_sqe->rkey = base_wr->wr.rdma.rkey; siw_sqe->opcode = map_send_opcode[base_wr->opcode].siw; if (siw_sqe->opcode > SIW_NUM_OPCODES) { verbs_err(verbs_get_ctx(base_qp->context), "libsiw: opcode %d unsupported\n", base_wr->opcode); return -EINVAL; } if (sig_all) flags |= SIW_WQE_SIGNALLED; if (flags & SIW_WQE_INLINE) { char *data = (char *)&siw_sqe->sge[1]; int bytes = 0, i = 0; /* Allow more than SIW_MAX_SGE, since content copied here */ while (i < base_wr->num_sge) { bytes += base_wr->sg_list[i].length; if (bytes > (int)SIW_MAX_INLINE) { verbs_err(verbs_get_ctx(base_qp->context), "libsiw: inline data: %d:%d\n", bytes, (int)SIW_MAX_INLINE); return -EINVAL; } memcpy(data, (void *)(uintptr_t)base_wr->sg_list[i].addr, base_wr->sg_list[i].length); data += base_wr->sg_list[i++].length; } siw_sqe->sge[0].length = bytes; } else { if (siw_sqe->num_sge > SIW_MAX_SGE) return -EINVAL; /* this assumes same layout of siw and base SGE */ memcpy(siw_sqe->sge, base_wr->sg_list, siw_sqe->num_sge * sizeof(struct ibv_sge)); } atomic_store(fp, flags); return 0; } static int siw_post_send(struct ibv_qp *base_qp, struct ibv_send_wr *wr, struct ibv_send_wr **bad_wr) { struct siw_qp *qp = qp_base2siw(base_qp); uint32_t sq_put; atomic_ushort *fp; int new_sqe = 0, rv = 0; *bad_wr = NULL; pthread_spin_lock(&qp->sq_lock); sq_put = qp->sq_put; /* * Push all current work requests into mmapped SQ */ while (wr) { uint32_t idx = sq_put % qp->num_sqe; struct siw_sqe *sqe = &qp->sendq[idx]; uint16_t sqe_flags; fp = (atomic_ushort *)&sqe->flags; sqe_flags = atomic_load(fp); if (!(sqe_flags & SIW_WQE_VALID)) { rv = push_send_wqe(base_qp, wr, sqe, qp->sq_sig_all); if (rv) { *bad_wr = wr; break; } new_sqe++; } else { verbs_err(verbs_get_ctx(base_qp->context), "libsiw: QP[%d]: SQ overflow, idx %d\n", qp->id, idx); rv = -ENOMEM; *bad_wr = wr; break; } sq_put++; wr = wr->next; } if (new_sqe) { /* * If last WQE pushed before position where current post_send * started is idle, we assume SQ is not being actively * processed. Only then, the doorbell call will be issued. * This may significantly reduce unnecessary doorbell calls * on a busy SQ. We also always ring the doorbell, if the * complete SQ was re-written during current post_send. */ if (new_sqe < qp->num_sqe) { uint32_t old_idx = (qp->sq_put - 1) % qp->num_sqe; struct siw_sqe *old_sqe = &qp->sendq[old_idx]; fp = (atomic_ushort *)&old_sqe->flags; if (!(atomic_load(fp) & SIW_WQE_VALID)) rv = siw_db(qp); } else { rv = siw_db(qp); } if (rv) *bad_wr = wr; qp->sq_put = sq_put; } pthread_spin_unlock(&qp->sq_lock); return rv; } static inline int push_recv_wqe(struct ibv_recv_wr *base_wr, struct siw_rqe *siw_rqe) { atomic_ushort *fp = (atomic_ushort *)&siw_rqe->flags; siw_rqe->id = base_wr->wr_id; siw_rqe->num_sge = base_wr->num_sge; if (base_wr->num_sge == 1) { siw_rqe->sge[0].laddr = base_wr->sg_list[0].addr; siw_rqe->sge[0].length = base_wr->sg_list[0].length; siw_rqe->sge[0].lkey = base_wr->sg_list[0].lkey; } else if (base_wr->num_sge && base_wr->num_sge <= SIW_MAX_SGE) /* this assumes same layout of siw and base SGE */ memcpy(siw_rqe->sge, base_wr->sg_list, sizeof(struct ibv_sge) * base_wr->num_sge); else return -EINVAL; atomic_store(fp, SIW_WQE_VALID); return 0; } static int siw_post_recv(struct ibv_qp *base_qp, struct ibv_recv_wr *wr, struct ibv_recv_wr **bad_wr) { struct siw_qp *qp = qp_base2siw(base_qp); uint32_t rq_put; int rv = 0; pthread_spin_lock(&qp->rq_lock); rq_put = qp->rq_put; while (wr) { int idx = rq_put % qp->num_rqe; struct siw_rqe *rqe = &qp->recvq[idx]; atomic_ushort *fp = (atomic_ushort *)&rqe->flags; uint16_t rqe_flags = atomic_load(fp); if (!(rqe_flags & SIW_WQE_VALID)) { if (push_recv_wqe(wr, rqe)) { *bad_wr = wr; rv = -EINVAL; break; } } else { verbs_err(verbs_get_ctx(base_qp->context), "libsiw: QP[%d]: RQ overflow, idx %d\n", qp->id, idx); rv = -ENOMEM; *bad_wr = wr; break; } rq_put++; wr = wr->next; } qp->rq_put = rq_put; pthread_spin_unlock(&qp->rq_lock); return rv; } static int siw_post_srq_recv(struct ibv_srq *base_srq, struct ibv_recv_wr *wr, struct ibv_recv_wr **bad_wr) { struct siw_srq *srq = srq_base2siw(base_srq); uint32_t srq_put; int rv = 0; pthread_spin_lock(&srq->lock); srq_put = srq->rq_put; while (wr) { int idx = srq_put % srq->num_rqe; struct siw_rqe *rqe = &srq->recvq[idx]; atomic_ushort *fp = (atomic_ushort *)&rqe->flags; uint16_t rqe_flags = atomic_load(fp); if (!(rqe_flags & SIW_WQE_VALID)) { if (push_recv_wqe(wr, rqe)) { *bad_wr = wr; rv = -EINVAL; break; } } else { verbs_err(verbs_get_ctx(base_srq->context), "libsiw: SRQ[%p]: SRQ overflow\n", srq); rv = -ENOMEM; *bad_wr = wr; break; } srq_put++; wr = wr->next; } srq->rq_put = srq_put; pthread_spin_unlock(&srq->lock); return rv; } static const struct { enum siw_opcode siw; enum ibv_wc_opcode base; } map_cqe_opcode[SIW_NUM_OPCODES] = { { SIW_OP_WRITE, IBV_WC_RDMA_WRITE }, { SIW_OP_READ, IBV_WC_RDMA_READ }, { SIW_OP_READ_LOCAL_INV, IBV_WC_RDMA_READ }, { SIW_OP_SEND, IBV_WC_SEND }, { SIW_OP_SEND_WITH_IMM, IBV_WC_SEND }, { SIW_OP_SEND_REMOTE_INV, IBV_WC_SEND }, { SIW_OP_FETCH_AND_ADD, IBV_WC_FETCH_ADD }, { SIW_OP_COMP_AND_SWAP, IBV_WC_COMP_SWAP }, { SIW_OP_RECEIVE, IBV_WC_RECV } }; static const struct { enum siw_wc_status siw; enum ibv_wc_status base; } map_cqe_status[SIW_NUM_WC_STATUS] = { { SIW_WC_SUCCESS, IBV_WC_SUCCESS }, { SIW_WC_LOC_LEN_ERR, IBV_WC_LOC_LEN_ERR }, { SIW_WC_LOC_PROT_ERR, IBV_WC_LOC_PROT_ERR }, { SIW_WC_LOC_QP_OP_ERR, IBV_WC_LOC_QP_OP_ERR }, { SIW_WC_WR_FLUSH_ERR, IBV_WC_WR_FLUSH_ERR }, { SIW_WC_BAD_RESP_ERR, IBV_WC_BAD_RESP_ERR }, { SIW_WC_LOC_ACCESS_ERR, IBV_WC_LOC_ACCESS_ERR }, { SIW_WC_REM_ACCESS_ERR, IBV_WC_REM_ACCESS_ERR }, { SIW_WC_REM_INV_REQ_ERR, IBV_WC_REM_INV_REQ_ERR }, { SIW_WC_GENERAL_ERR, IBV_WC_GENERAL_ERR } }; static inline void copy_cqe(struct siw_cqe *cqe, struct ibv_wc *wc) { wc->wr_id = cqe->id; wc->byte_len = cqe->bytes; /* No immediate data supported yet */ wc->wc_flags = 0; wc->imm_data = 0; wc->vendor_err = 0; wc->opcode = map_cqe_opcode[cqe->opcode].base; wc->status = map_cqe_status[cqe->status].base; wc->qp_num = (uint32_t)cqe->qp_id; } static int siw_poll_cq(struct ibv_cq *ibcq, int num_entries, struct ibv_wc *wc) { struct siw_cq *cq = cq_base2siw(ibcq); int new = 0; pthread_spin_lock(&cq->lock); for (; num_entries--; wc++) { struct siw_cqe *cqe = &cq->queue[cq->cq_get % cq->num_cqe]; atomic_uchar *fp = (atomic_uchar *)&cqe->flags; if (atomic_load(fp) & SIW_WQE_VALID) { copy_cqe(cqe, wc); atomic_store(fp, 0); cq->cq_get++; new++; } else break; } pthread_spin_unlock(&cq->lock); return new; } static const struct verbs_context_ops siw_context_ops = { .alloc_pd = siw_alloc_pd, .async_event = siw_async_event, .create_cq = siw_create_cq, .create_qp = siw_create_qp, .create_srq = siw_create_srq, .dealloc_pd = siw_free_pd, .dereg_mr = siw_dereg_mr, .destroy_cq = siw_destroy_cq, .destroy_qp = siw_destroy_qp, .destroy_srq = siw_destroy_srq, .free_context = siw_free_context, .modify_qp = siw_modify_qp, .modify_srq = siw_modify_srq, .poll_cq = siw_poll_cq, .post_recv = siw_post_recv, .post_send = siw_post_send, .post_srq_recv = siw_post_srq_recv, .query_device_ex = siw_query_device, .query_port = siw_query_port, .query_qp = siw_query_qp, .reg_mr = siw_reg_mr, .req_notify_cq = siw_notify_cq, }; static struct verbs_context *siw_alloc_context(struct ibv_device *base_dev, int fd, void *pdata) { struct siw_context *ctx; struct ibv_get_context cmd = {}; struct siw_cmd_alloc_context_resp resp = {}; ctx = verbs_init_and_alloc_context(base_dev, fd, ctx, base_ctx, RDMA_DRIVER_SIW); if (!ctx) return NULL; if (ibv_cmd_get_context(&ctx->base_ctx, &cmd, sizeof(cmd), &resp.ibv_resp, sizeof(resp))) { verbs_uninit_context(&ctx->base_ctx); free(ctx); return NULL; } verbs_set_ops(&ctx->base_ctx, &siw_context_ops); ctx->dev_id = resp.dev_id; return &ctx->base_ctx; } static void siw_free_context(struct ibv_context *ibv_ctx) { struct siw_context *ctx = ctx_ibv2siw(ibv_ctx); verbs_uninit_context(&ctx->base_ctx); free(ctx); } static struct verbs_device *siw_device_alloc(struct verbs_sysfs_dev *unused) { struct siw_device *dev; dev = calloc(1, sizeof(*dev)); if (!dev) return NULL; return &dev->base_dev; } static void siw_device_free(struct verbs_device *vdev) { struct siw_device *dev = container_of(vdev, struct siw_device, base_dev); free(dev); } static const struct verbs_match_ent rnic_table[] = { VERBS_DRIVER_ID(RDMA_DRIVER_SIW), {}, }; static const struct verbs_device_ops siw_dev_ops = { .name = "siw", .match_min_abi_version = SIW_ABI_VERSION, .match_max_abi_version = SIW_ABI_VERSION, .match_table = rnic_table, .alloc_device = siw_device_alloc, .uninit_device = siw_device_free, .alloc_context = siw_alloc_context, }; PROVIDER_DRIVER(siw, siw_dev_ops);