Add QPEx class alongside the new post send functions. The new post send flow is as follows: - Create a WR on the extended QP. - Set the WR's attributes. - Commit the entries in the send buffer and ring the doorbell. QPEx class exposes the relevant methods (wr_*) for that. Signed-off-by: Ahmad Ghazawi <ahmadg@xxxxxxxxxxxx> Signed-off-by: Noa Osherovich <noaos@xxxxxxxxxxxx> Reviewed-by: Edward Srouji <edwards@xxxxxxxxxxxx> --- pyverbs/libibverbs.pxd | 40 +++++++++ pyverbs/libibverbs_enums.pxd | 14 ++++ pyverbs/qp.pxd | 6 ++ pyverbs/qp.pyx | 154 +++++++++++++++++++++++++++++++++-- 4 files changed, 209 insertions(+), 5 deletions(-) diff --git a/pyverbs/libibverbs.pxd b/pyverbs/libibverbs.pxd index 8949759511a5..c038c4aa4d5b 100755 --- a/pyverbs/libibverbs.pxd +++ b/pyverbs/libibverbs.pxd @@ -438,6 +438,10 @@ cdef extern from 'infiniband/verbs.h': unsigned int handle unsigned int events_completed + cdef struct ibv_data_buf: + void *addr + size_t length + cdef struct ibv_qp: ibv_context *context; void *qp_context; @@ -460,6 +464,12 @@ cdef extern from 'infiniband/verbs.h': uint64_t resource_type); void *pd_context; + cdef struct ibv_qp_ex: + ibv_qp qp_base + uint64_t comp_mask + uint64_t wr_id + unsigned int wr_flags + ibv_device **ibv_get_device_list(int *n) void ibv_free_device_list(ibv_device **list) ibv_context *ibv_open_device(ibv_device *device) @@ -549,3 +559,33 @@ cdef extern from 'infiniband/verbs.h': ibv_pd *ibv_alloc_parent_domain(ibv_context *context, ibv_parent_domain_init_attr *attr) uint32_t ibv_inc_rkey(uint32_t rkey) + ibv_qp_ex *ibv_qp_to_qp_ex(ibv_qp *qp) + void ibv_wr_atomic_cmp_swp(ibv_qp_ex *qp, uint32_t rkey, + uint64_t remote_addr, uint64_t compare, + uint64_t swap) + void ibv_wr_atomic_fetch_add(ibv_qp_ex *qp, uint32_t rkey, + uint64_t remote_addr, uint64_t add) + void ibv_wr_bind_mw(ibv_qp_ex *qp, ibv_mw *mw, uint32_t rkey, + ibv_mw_bind_info *bind_info) + void ibv_wr_local_inv(ibv_qp_ex *qp, uint32_t invalidate_rkey) + void ibv_wr_rdma_read(ibv_qp_ex *qp, uint32_t rkey, uint64_t remote_addr) + void ibv_wr_rdma_write(ibv_qp_ex *qp, uint32_t rkey, uint64_t remote_addr) + void ibv_wr_rdma_write_imm(ibv_qp_ex *qp, uint32_t rkey, + uint64_t remote_addr, uint32_t imm_data) + void ibv_wr_send(ibv_qp_ex *qp) + void ibv_wr_send_imm(ibv_qp_ex *qp, uint32_t imm_data) + void ibv_wr_send_inv(ibv_qp_ex *qp, uint32_t invalidate_rkey) + void ibv_wr_send_tso(ibv_qp_ex *qp, void *hdr, uint16_t hdr_sz, + uint16_t mss) + void ibv_wr_set_ud_addr(ibv_qp_ex *qp, ibv_ah *ah, uint32_t remote_qpn, + uint32_t remote_qkey) + void ibv_wr_set_xrc_srqn(ibv_qp_ex *qp, uint32_t remote_srqn) + void ibv_wr_set_inline_data(ibv_qp_ex *qp, void *addr, size_t length) + void ibv_wr_set_inline_data_list(ibv_qp_ex *qp, size_t num_buf, + ibv_data_buf *buf_list) + void ibv_wr_set_sge(ibv_qp_ex *qp, uint32_t lkey, uint64_t addr, + uint32_t length) + void ibv_wr_set_sge_list(ibv_qp_ex *qp, size_t num_sge, ibv_sge *sg_list) + void ibv_wr_start(ibv_qp_ex *qp) + int ibv_wr_complete(ibv_qp_ex *qp) + void ibv_wr_abort(ibv_qp_ex *qp) diff --git a/pyverbs/libibverbs_enums.pxd b/pyverbs/libibverbs_enums.pxd index fd6a6f49a163..a706cec0aba8 100755 --- a/pyverbs/libibverbs_enums.pxd +++ b/pyverbs/libibverbs_enums.pxd @@ -236,6 +236,7 @@ cdef extern from '<infiniband/verbs.h>': IBV_QP_INIT_ATTR_MAX_TSO_HEADER IBV_QP_INIT_ATTR_IND_TABLE IBV_QP_INIT_ATTR_RX_HASH + IBV_QP_INIT_ATTR_SEND_OPS_FLAGS cpdef enum ibv_qp_create_flags: IBV_QP_CREATE_BLOCK_SELF_MCAST_LB @@ -401,6 +402,19 @@ cdef extern from '<infiniband/verbs.h>': cpdef enum: IBV_WC_STANDARD_FLAGS + cpdef enum ibv_qp_create_send_ops_flags: + IBV_QP_EX_WITH_RDMA_WRITE + IBV_QP_EX_WITH_RDMA_WRITE_WITH_IMM + IBV_QP_EX_WITH_SEND + IBV_QP_EX_WITH_SEND_WITH_IMM + IBV_QP_EX_WITH_RDMA_READ + IBV_QP_EX_WITH_ATOMIC_CMP_AND_SWP + IBV_QP_EX_WITH_ATOMIC_FETCH_AND_ADD + IBV_QP_EX_WITH_LOCAL_INV + IBV_QP_EX_WITH_BIND_MW + IBV_QP_EX_WITH_SEND_WITH_INV + IBV_QP_EX_WITH_TSO + cdef unsigned long long IBV_DEVICE_RAW_SCATTER_FCS cdef unsigned long long IBV_DEVICE_PCI_WRITE_END_PADDING diff --git a/pyverbs/qp.pxd b/pyverbs/qp.pxd index 52aab503e40d..209a2438dd83 100644 --- a/pyverbs/qp.pxd +++ b/pyverbs/qp.pxd @@ -37,3 +37,9 @@ cdef class QP(PyverbsCM): cdef update_cqs(self, init_attr) cdef object scq cdef object rcq + +cdef class DataBuffer(PyverbsCM): + cdef v.ibv_data_buf data + +cdef class QPEx(QP): + cdef v.ibv_qp_ex *qp_ex diff --git a/pyverbs/qp.pyx b/pyverbs/qp.pyx index 9d368b62022d..1fcb23909758 100755 --- a/pyverbs/qp.pyx +++ b/pyverbs/qp.pyx @@ -1,20 +1,30 @@ # SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) # Copyright (c) 2019 Mellanox Technologies, Inc. All rights reserved. + +from libc.stdlib cimport malloc, free +from libc.string cimport memcpy + from pyverbs.utils import gid_str, qp_type_to_str, qp_state_to_str, mtu_to_str +from pyverbs.pyverbs_error import PyverbsUserError, PyverbsError from pyverbs.utils import access_flags_to_str, mig_state_to_str -from pyverbs.pyverbs_error import PyverbsUserError +from pyverbs.wr cimport RecvWR, SendWR, SGE from pyverbs.base import PyverbsRDMAErrno -from pyverbs.wr cimport RecvWR, SendWR +from pyverbs.addr cimport AHAttr, GID, AH +from pyverbs.mr cimport MW, MWBindInfo cimport pyverbs.libibverbs_enums as e -from pyverbs.addr cimport AHAttr, GID from pyverbs.addr cimport GlobalRoute from pyverbs.device cimport Context +from cpython.ref cimport PyObject from pyverbs.cq cimport CQ, CQEX cimport pyverbs.libibverbs as v from pyverbs.xrcd cimport XRCD from pyverbs.srq cimport SRQ from pyverbs.pd cimport PD -from libc.string cimport memcpy + +cdef extern from 'Python.h': + void* PyLong_AsVoidPtr(object) +cdef extern from 'endian.h': + unsigned long htobe32(unsigned long host_32bits) cdef class QPCap(PyverbsObject): @@ -240,7 +250,7 @@ cdef class QPInitAttrEx(PyverbsObject): SRQ srq=None, QPCap cap=None, sq_sig_all=0, comp_mask=0, PD pd=None, XRCD xrcd=None, create_flags=0, max_tso_header=0, source_qpn=0, object hash_conf=None, - object ind_table=None): + object ind_table=None, send_ops_flags=0): """ Initialize a QPInitAttrEx object with user-defined or default values. :param qp_type: QP type to be created @@ -261,6 +271,8 @@ cdef class QPInitAttrEx(PyverbsObject): set in create_flags) :param hash_conf: Not yet supported :param ind_table: Not yet supported + :param send_ops_flags: Send opcodes to be supported by the extended QP. + Use ibv_qp_create_send_ops_flags enum :return: An initialized QPInitAttrEx object """ super().__init__() @@ -302,6 +314,7 @@ cdef class QPInitAttrEx(PyverbsObject): self.attr.create_flags = create_flags self.attr.max_tso_header = max_tso_header self.attr.source_qpn = source_qpn + self.attr.send_ops_flags = send_ops_flags @property def send_cq(self): @@ -1141,6 +1154,137 @@ cdef class QP(PyverbsCM): print_format.format(' state', qp_state_to_str(self.qp_state)) +cdef class DataBuffer(PyverbsCM): + def __init__(self, addr, length): + super().__init__() + self.data.addr = PyLong_AsVoidPtr(addr) + self.data.length = length + + +cdef class QPEx(QP): + def __init__(self, object creator not None, object init_attr not None, + QPAttr qp_attr=None): + """ + Initializes a QPEx object. Since this is an extension of a QP, QP + creation is done in the parent class. The extended QP is retrieved by + casting the ibv_qp to ibv_qp_ex. + :return: An initialized QPEx object + """ + super().__init__(creator, init_attr, qp_attr) + self.qp_ex = v.ibv_qp_to_qp_ex(self.qp) + if self.qp_ex == NULL: + raise PyverbsRDMAErrno('Failed to create extended QP') + + @property + def comp_mask(self): + return self.qp_ex.comp_mask + @comp_mask.setter + def comp_mask(self, val): + self.qp_ex.comp_mask = val + + @property + def wr_id(self): + return self.qp_ex.wr_id + @wr_id.setter + def wr_id(self, val): + self.qp_ex.wr_id = val + + @property + def wr_flags(self): + return self.qp_ex.wr_flags + @wr_flags.setter + def wr_flags(self, val): + self.qp_ex.wr_flags = val + + def wr_atomic_cmp_swp(self, rkey, remote_addr, compare, swap): + v.ibv_wr_atomic_cmp_swp(self.qp_ex, rkey, remote_addr, compare, swap) + + def wr_atomic_fetch_add(self, rkey, remote_addr, add): + v.ibv_wr_atomic_fetch_add(self.qp_ex, rkey, remote_addr, add) + + def wr_bind_mw(self, MW mw, rkey, MWBindInfo bind_info): + cdef v.ibv_mw_bind_info *info + info = &bind_info.info + v.ibv_wr_bind_mw(self.qp_ex, <v.ibv_mw*>mw.mw, rkey, + <v.ibv_mw_bind_info*>info) + + def wr_local_inv(self, invalidate_rkey): + v.ibv_wr_local_inv(self.qp_ex, invalidate_rkey) + + def wr_rdma_read(self, rkey, remote_addr): + v.ibv_wr_rdma_read(self.qp_ex, rkey, remote_addr) + + def wr_rdma_write(self, rkey, remote_addr): + v.ibv_wr_rdma_write(self.qp_ex, rkey, remote_addr) + + def wr_rdma_write_imm(self, rkey, remote_addr, data): + cdef unsigned int imm_data = htobe32(data) + v.ibv_wr_rdma_write_imm(self.qp_ex, rkey, remote_addr, imm_data) + + def wr_send(self): + v.ibv_wr_send(self.qp_ex) + + def wr_send_imm(self, data): + cdef unsigned int imm_data = htobe32(data) + return v.ibv_wr_send_imm(self.qp_ex, imm_data) + + def wr_send_inv(self, invalidate_rkey): + v.ibv_wr_send_inv(self.qp_ex, invalidate_rkey) + + def wr_send_tso(self, hdr, hdr_sz, mss): + ptr = PyLong_AsVoidPtr(hdr) + v.ibv_wr_send_tso(self.qp_ex, ptr, hdr_sz, mss) + + def wr_set_ud_addr(self, AH ah, remote_qpn, remote_rkey): + v.ibv_wr_set_ud_addr(self.qp_ex, ah.ah, remote_qpn, remote_rkey) + + def wr_set_xrc_srqn(self, remote_srqn): + v.ibv_wr_set_xrc_srqn(self.qp_ex, remote_srqn) + + def wr_set_inline_data(self, addr, length): + ptr = PyLong_AsVoidPtr(addr) + v.ibv_wr_set_inline_data(self.qp_ex, ptr, length) + + def wr_set_inline_data_list(self, num_buf, buf_list): + cdef v.ibv_data_buf *data = NULL + data = <v.ibv_data_buf*>malloc(num_buf * sizeof(v.ibv_data_buf)) + if data == NULL: + raise PyverbsError('Failed to allocate data buffer') + for i in range(num_buf): + data_buf = <DataBuffer>buf_list[i] + data[i].addr = data_buf.data.addr + data[i].length = data_buf.data.length + v.ibv_wr_set_inline_data_list(self.qp_ex, num_buf, data) + free(data) + + def wr_set_sge(self, SGE sge not None): + v.ibv_wr_set_sge(self.qp_ex, sge.lkey, sge.addr, sge.length) + + def wr_set_sge_list(self, num_sge, sg_list): + cdef v.ibv_sge *sge = NULL + sge = <v.ibv_sge*>malloc(num_sge * sizeof(v.ibv_sge)) + if sge == NULL: + raise PyverbsError('Failed to allocate SGE buffer') + for i in range(num_sge): + sge[i].addr = sg_list[i].addr + sge[i].length = sg_list[i].length + sge[i].lkey = sg_list[i].lkey + v.ibv_wr_set_sge_list(self.qp_ex, num_sge, sge) + free(sge) + + def wr_start(self): + v.ibv_wr_start(self.qp_ex) + + def wr_complete(self): + rc = v.ibv_wr_complete(self.qp_ex) + if rc != 0: + raise PyverbsRDMAErrno('ibv_wr_complete failed , returned {}'. + format(rc)) + + def wr_abort(self): + v.ibv_wr_abort(self.qp_ex) + + def _copy_caps(QPCap src, dst): """ Copy the QPCaps values of src into the inner ibv_qp_cap struct of dst. -- 2.21.0