Add support for extended CQ which includes: - CqInitAttrEx class - CQEX Signed-off-by: Noa Osherovich <noaos@xxxxxxxxxxxx> Reviewed-by: Maor Gottlieb <maorg@xxxxxxxxxxxx> Reviewed-by: Leon Romanovsky <leonro@xxxxxxxxxxxx> --- pyverbs/cq.pxd | 15 +++ pyverbs/cq.pyx | 249 ++++++++++++++++++++++++++++++++++- pyverbs/device.pyx | 4 +- pyverbs/libibverbs.pxd | 50 +++++++ pyverbs/libibverbs_enums.pxd | 31 +++-- 5 files changed, 330 insertions(+), 19 deletions(-) diff --git a/pyverbs/cq.pxd b/pyverbs/cq.pxd index 82607042ce6b..181f7da668fe 100644 --- a/pyverbs/cq.pxd +++ b/pyverbs/cq.pxd @@ -13,5 +13,20 @@ cdef class CQ(PyverbsCM): cpdef close(self) cdef object context +cdef class CqInitAttrEx(PyverbsObject): + cdef v.ibv_cq_init_attr_ex attr + +cdef class CQEX(PyverbsCM): + cdef v.ibv_cq_ex *cq + cdef v.ibv_cq *ibv_cq + cpdef close(self) + cdef object context + cdef class WC(PyverbsObject): cdef v.ibv_wc wc + +cdef class PollCqAttr(PyverbsObject): + cdef v.ibv_poll_cq_attr attr + +cdef class WcTmInfo(PyverbsObject): + cdef v.ibv_wc_tm_info info diff --git a/pyverbs/cq.pyx b/pyverbs/cq.pyx index d1e2520f6be1..748e30fe9bcb 100644 --- a/pyverbs/cq.pyx +++ b/pyverbs/cq.pyx @@ -126,6 +126,199 @@ cdef class CQ(PyverbsCM): print_format.format('CQEs', self.cq.cqe) +cdef class CqInitAttrEx(PyverbsObject): + def __cinit__(self, cqe = 100, CompChannel channel = None, comp_vector = 0, + wc_flags = 0, comp_mask = 0, flags = 0): + """ + Initializes a CqInitAttrEx object with the given parameters. + :param cqe: CQ's capacity + :param channel: If set, will be used to return completion events + :param comp_vector: Will be used for signaling completion events. + Must be larger than 0 and smaller than the + context's num_comp_vectors + :param wc_flags: The wc_flags that should be returned in ibv_poll_cq_ex. + Or'ed bit of enum ibv_wc_flags_ex. + :param comp_mask: compatibility mask (extended verb) + :param flags: create cq attr flags - one or more flags from + ibv_create_cq_attr_flags enum + :return: + """ + self.attr.cqe = cqe + self.attr.cq_context = NULL + self.attr.channel = NULL if channel is None else channel.cc + self.attr.comp_vector = comp_vector + self.attr.wc_flags = wc_flags + self.attr.comp_mask = comp_mask + self.attr.flags = flags + + @property + def cqe(self): + return self.attr.cqe + @cqe.setter + def cqe(self, val): + self.attr.cqe = val + + # Setter-only properties require the older syntax + property cq_context: + def __set__(self, val): + self.attr.cq_context = <void*>val + + property channel: + def __set__(self, val): + self.attr.channel = <v.ibv_comp_channel*>val + + @property + def comp_vector(self): + return self.attr.comp_vector + @comp_vector.setter + def comp_vector(self, val): + self.attr.comp_vector = val + + @property + def wc_flags(self): + return self.attr.wc_flags + @wc_flags.setter + def wc_flags(self, val): + self.attr.wc_flags = val + + @property + def comp_mask(self): + return self.attr.comp_mask + @comp_mask.setter + def comp_mask(self, val): + self.attr.comp_mask = val + + @property + def flags(self): + return self.attr.flags + @flags.setter + def flags(self, val): + self.attr.flags = val + + def __str__(self): + print_format = '{:22}: {:<20}\n' + return print_format.format('Number of CQEs', self.cqe) +\ + print_format.format('WC flags', create_wc_flags_to_str(self.wc_flags)) +\ + print_format.format('comp mask', self.comp_mask) +\ + print_format.format('flags', self.flags) + + +cdef class CQEX(PyverbsCM): + def __cinit__(self, Context context not None, CqInitAttrEx init_attr): + """ + Initializes a CQEX object on the given device's context with the given + attributes. + :param context: The device's context on which to open the CQ + :param init_attr: Initial attributes that describe the CQ + :return: The newly created CQEX on success + """ + if init_attr is None: + init_attr = CqInitAttrEx() + self.cq = v.ibv_create_cq_ex(context.context, &init_attr.attr) + if self.cq == NULL: + raise PyverbsRDMAErrno('Failed to create extended CQ') + self.ibv_cq = v.ibv_cq_ex_to_cq(self.cq) + self.context = context + context.add_ref(self) + + def __dealloc__(self): + self.close() + + cpdef close(self): + self.logger.debug('Closing CQEx') + if self.cq != NULL: + rc = v.ibv_destroy_cq(<v.ibv_cq*>self.cq) + if rc != 0: + raise PyverbsRDMAErrno('Failed to destroy CQEX') + self.cq = NULL + self.context = None + + def start_poll(self, PollCqAttr attr): + """ + Start polling a batch of work completions. + :param attr: For easy future extensions + :return: 0 on success, ENOENT when no completions are available + """ + if attr is None: + attr = PollCqAttr() + return v.ibv_start_poll(self.cq, &attr.attr) + + def poll_next(self): + """ + Get the next work completion. + :return: 0 on success, ENOENT when no completions are available + """ + return v.ibv_next_poll(self.cq) + + def end_poll(self): + """ + Indicates the end of polling batch of work completions + :return: None + """ + return v.ibv_end_poll(self.cq) + + def read_opcode(self): + return v.ibv_wc_read_opcode(self.cq) + def read_vendor_err(self): + return v.ibv_wc_read_vendor_err(self.cq) + def read_byte_len(self): + return v.ibv_wc_read_byte_len(self.cq) + def read_imm_data(self): + return v.ibv_wc_read_imm_data(self.cq) + def read_qp_num(self): + return v.ibv_wc_read_qp_num(self.cq) + def read_src_qp(self): + return v.ibv_wc_read_src_qp(self.cq) + def read_wc_flags(self): + return v.ibv_wc_read_wc_flags(self.cq) + def read_slid(self): + return v.ibv_wc_read_slid(self.cq) + def read_sl(self): + return v.ibv_wc_read_sl(self.cq) + def read_dlid_path_bits(self): + return v.ibv_wc_read_dlid_path_bits(self.cq) + def read_timestamp(self): + return v.ibv_wc_read_completion_ts(self.cq) + def read_cvlan(self): + return v.ibv_wc_read_cvlan(self.cq) + def read_flow_tag(self): + return v.ibv_wc_read_flow_tag(self.cq) + def read_tm_info(self): + info = WcTmInfo() + v.ibv_wc_read_tm_info(self.cq, &info.info) + return info + def read_completion_wallclock_ns(self): + return v.ibv_wc_read_completion_wallclock_ns(self.cq) + + @property + def status(self): + return self.cq.status + @status.setter + def status(self, val): + self.cq.status = val + + @property + def wr_id(self): + return self.cq.wr_id + @wr_id.setter + def wr_id(self, val): + self.cq.wr_id = val + + @property + def _cq(self): + return <object>self.cq + + @property + def _ibv_cq(self): + return <object>self.ibv_cq + + def __str__(self): + print_format = '{:<22}: {:<20}\n' + return 'Extended CQ:\n' +\ + print_format.format('Handle', self.cq.handle) +\ + print_format.format('CQEs', self.cq.cqe) + + cdef class WC(PyverbsObject): def __cinit__(self, wr_id=0, status=0, opcode=0, vendor_err=0, byte_len=0, qp_num=0, src_qp=0, imm_data=0, wc_flags=0, pkey_index=0, @@ -243,6 +436,31 @@ cdef class WC(PyverbsObject): print_format.format('dlid path bits', self.dlid_path_bits) +cdef class PollCqAttr(PyverbsObject): + @property + def comp_mask(self): + return self.attr.comp_mask + @comp_mask.setter + def comp_mask(self, val): + self.attr.comp_mask = val + + +cdef class WcTmInfo(PyverbsObject): + @property + def tag(self): + return self.info.tag + @tag.setter + def tag(self, val): + self.info.tag = val + + @property + def priv(self): + return self.info.priv + @priv.setter + def priv(self, val): + self.info.priv = val + + def cqe_status_to_str(status): try: return {e.IBV_WC_SUCCESS: "success", @@ -283,13 +501,32 @@ def cqe_opcode_to_str(opcode): except KeyError: return "Unknown CQE opcode {op}".format(op=opcode) -def cqe_flags_to_str(flags): - cqe_flags = {1: "GRH", 2: "With immediate", 4: "IP csum OK", - 8: "With invalidate", 16: "TM sync request", 32: "TM match", - 64: "TM data valid"} +def flags_to_str(flags, dictionary): flags_str = "" - for f in cqe_flags: + for f in dictionary: if flags & f: - flags_str += cqe_flags[f] + flags_str += dictionary[f] flags_str += " " return flags_str + + +def cqe_flags_to_str(flags): + cqe_flags = {1: "GRH", 2: "With immediate", 4: "IP csum OK", + 8: "With invalidate", 16: "TM sync request", 32: "TM match", + 64: "TM data valid"} + return flags_to_str(flags, cqe_flags) + +def create_wc_flags_to_str(flags): + cqe_flags = {e.IBV_WC_EX_WITH_BYTE_LEN: 'IBV_WC_EX_WITH_BYTE_LEN', + e.IBV_WC_EX_WITH_IMM: 'IBV_WC_EX_WITH_IMM', + e.IBV_WC_EX_WITH_QP_NUM: 'IBV_WC_EX_WITH_QP_NUM', + e.IBV_WC_EX_WITH_SRC_QP: 'IBV_WC_EX_WITH_SRC_QP', + e.IBV_WC_EX_WITH_SLID: 'IBV_WC_EX_WITH_SLID', + e.IBV_WC_EX_WITH_SL: 'IBV_WC_EX_WITH_SL', + e.IBV_WC_EX_WITH_DLID_PATH_BITS: 'IBV_WC_EX_WITH_DLID_PATH_BITS', + e.IBV_WC_EX_WITH_COMPLETION_TIMESTAMP: 'IBV_WC_EX_WITH_COMPLETION_TIMESTAMP', + e.IBV_WC_EX_WITH_CVLAN: 'IBV_WC_EX_WITH_CVLAN', + e.IBV_WC_EX_WITH_FLOW_TAG: 'IBV_WC_EX_WITH_FLOW_TAG', + e.IBV_WC_EX_WITH_TM_INFO: 'IBV_WC_EX_WITH_TM_INFO', + e.IBV_WC_EX_WITH_COMPLETION_TIMESTAMP_WALLCLOCK: 'IBV_WC_EX_WITH_COMPLETION_TIMESTAMP_WALLCLOCK'} + return flags_to_str(flags, cqe_flags) diff --git a/pyverbs/device.pyx b/pyverbs/device.pyx index 3808f8600840..e953b7fc2eaf 100644 --- a/pyverbs/device.pyx +++ b/pyverbs/device.pyx @@ -9,9 +9,9 @@ which returns a DeviceAttr object. import weakref from .pyverbs_error import PyverbsRDMAError, PyverbsError +from pyverbs.cq cimport CQEX, CQ, CompChannel from .pyverbs_error import PyverbsUserError from pyverbs.base import PyverbsRDMAErrno -from pyverbs.cq cimport CQ, CompChannel cimport pyverbs.libibverbs_enums as e cimport pyverbs.libibverbs as v from pyverbs.addr cimport GID @@ -192,7 +192,7 @@ cdef class Context(PyverbsCM): self.dms.add(obj) elif isinstance(obj, CompChannel): self.ccs.add(obj) - elif isinstance(obj, CQ): + elif isinstance(obj, CQ) or isinstance(obj, CQEX): self.cqs.add(obj) else: raise PyverbsError('Unrecognized object type') diff --git a/pyverbs/libibverbs.pxd b/pyverbs/libibverbs.pxd index 304dce5150d2..7facff5f8856 100644 --- a/pyverbs/libibverbs.pxd +++ b/pyverbs/libibverbs.pxd @@ -200,6 +200,34 @@ cdef extern from 'infiniband/verbs.h': unsigned int sl unsigned int dlid_path_bits + cdef struct ibv_cq_init_attr_ex: + unsigned int cqe + void *cq_context + ibv_comp_channel *channel + unsigned int comp_vector + unsigned long wc_flags + unsigned int comp_mask + unsigned int flags + + cdef struct ibv_cq_ex: + ibv_context *context + ibv_comp_channel *channel + void *cq_context + unsigned int handle + int cqe + unsigned int comp_events_completed + unsigned int async_events_completed + unsigned int comp_mask + ibv_wc_status status + unsigned long wr_id + + cdef struct ibv_poll_cq_attr: + unsigned int comp_mask + + cdef struct ibv_wc_tm_info: + unsigned long tag + unsigned int priv + ibv_device **ibv_get_device_list(int *n) void ibv_free_device_list(ibv_device **list) ibv_context *ibv_open_device(ibv_device *device) @@ -235,3 +263,25 @@ cdef extern from 'infiniband/verbs.h': ibv_comp_channel *channel, int comp_vector) int ibv_destroy_cq(ibv_cq *cq) int ibv_poll_cq(ibv_cq *cq, int num_entries, ibv_wc *wc) + ibv_cq_ex *ibv_create_cq_ex(ibv_context *context, + ibv_cq_init_attr_ex *cq_attr) + ibv_cq *ibv_cq_ex_to_cq(ibv_cq_ex *cq) + int ibv_start_poll(ibv_cq_ex *cq, ibv_poll_cq_attr *attr) + int ibv_next_poll(ibv_cq_ex *cq) + void ibv_end_poll(ibv_cq_ex *cq) + ibv_wc_opcode ibv_wc_read_opcode(ibv_cq_ex *cq) + unsigned int ibv_wc_read_vendor_err(ibv_cq_ex *cq) + unsigned int ibv_wc_read_byte_len(ibv_cq_ex *cq) + unsigned int ibv_wc_read_imm_data(ibv_cq_ex *cq) + unsigned int ibv_wc_read_invalidated_rkey(ibv_cq_ex *cq) + unsigned int ibv_wc_read_qp_num(ibv_cq_ex *cq) + unsigned int ibv_wc_read_src_qp(ibv_cq_ex *cq) + unsigned int ibv_wc_read_wc_flags(ibv_cq_ex *cq) + unsigned int ibv_wc_read_slid(ibv_cq_ex *cq) + unsigned char ibv_wc_read_sl(ibv_cq_ex *cq) + unsigned char ibv_wc_read_dlid_path_bits(ibv_cq_ex *cq) + unsigned long ibv_wc_read_completion_ts(ibv_cq_ex *cq) + unsigned short ibv_wc_read_cvlan(ibv_cq_ex *cq) + unsigned int ibv_wc_read_flow_tag(ibv_cq_ex *cq) + void ibv_wc_read_tm_info(ibv_cq_ex *cq, ibv_wc_tm_info *tm_info) + unsigned long ibv_wc_read_completion_wallclock_ns(ibv_cq_ex *cq) diff --git a/pyverbs/libibverbs_enums.pxd b/pyverbs/libibverbs_enums.pxd index 580252468e31..e429cd35cc47 100644 --- a/pyverbs/libibverbs_enums.pxd +++ b/pyverbs/libibverbs_enums.pxd @@ -173,17 +173,18 @@ cdef extern from '<infiniband/verbs.h>': IBV_WC_RECV_RDMA_WITH_IMM cpdef enum ibv_create_cq_wc_flags: - IBV_WC_EX_WITH_BYTE_LEN = 1 << 0 - IBV_WC_EX_WITH_IMM = 1 << 1 - IBV_WC_EX_WITH_QP_NUM = 1 << 2 - IBV_WC_EX_WITH_SRC_QP = 1 << 3 - IBV_WC_EX_WITH_SLID = 1 << 4 - IBV_WC_EX_WITH_SL = 1 << 5 - IBV_WC_EX_WITH_DLID_PATH_BITS = 1 << 6 - IBV_WC_EX_WITH_COMPLETION_TIMESTAMP = 1 << 7 - IBV_WC_EX_WITH_CVLAN = 1 << 8 - IBV_WC_EX_WITH_FLOW_TAG = 1 << 9 - IBV_WC_EX_WITH_TM_INFO = 1 << 10 + IBV_WC_EX_WITH_BYTE_LEN = 1 << 0 + IBV_WC_EX_WITH_IMM = 1 << 1 + IBV_WC_EX_WITH_QP_NUM = 1 << 2 + IBV_WC_EX_WITH_SRC_QP = 1 << 3 + IBV_WC_EX_WITH_SLID = 1 << 4 + IBV_WC_EX_WITH_SL = 1 << 5 + IBV_WC_EX_WITH_DLID_PATH_BITS = 1 << 6 + IBV_WC_EX_WITH_COMPLETION_TIMESTAMP = 1 << 7 + IBV_WC_EX_WITH_CVLAN = 1 << 8 + IBV_WC_EX_WITH_FLOW_TAG = 1 << 9 + IBV_WC_EX_WITH_TM_INFO = 1 << 10 + IBV_WC_EX_WITH_COMPLETION_TIMESTAMP_WALLCLOCK = 1 << 11 cpdef enum ibv_wc_flags: IBV_WC_GRH = 1 << 0 @@ -340,6 +341,14 @@ cdef extern from '<infiniband/verbs.h>': cpdef enum ibv_read_counters_flags: IBV_READ_COUNTERS_ATTR_PREFER_CACHED = 1 << 0 + cpdef enum ibv_cq_init_attr_mask: + IBV_CQ_INIT_ATTR_MASK_FLAGS = 1 << 0 + + cpdef enum ibv_create_cq_attr_flags: + IBV_CREATE_CQ_ATTR_SINGLE_THREADED = 1 << 0 + IBV_CREATE_CQ_ATTR_IGNORE_OVERRUN = 1 << 1 + + cdef extern from "<infiniband/tm_types.h>": cpdef enum ibv_tmh_op: IBV_TMH_NO_TAG = 0 -- 2.17.2