@@ -4,6 +4,7 @@
rdma_cython_module(pyverbs
addr.pyx
base.pyx
+ cq.pyx
device.pyx
enums.pyx
mr.pyx
new file mode 100644
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
+# Copyright (c) 2019, Mellanox Technologies. All rights reserved.
+from pyverbs.base cimport PyverbsObject, PyverbsCM
+cimport pyverbs.libibverbs as v
+
+cdef class CompChannel(PyverbsCM):
+ cdef v.ibv_comp_channel *cc
+ cpdef close(self)
+ cdef object context
+
+cdef class CQ(PyverbsCM):
+ cdef v.ibv_cq *cq
+ cpdef close(self)
+ cdef object context
+
+cdef class WC(PyverbsObject):
+ cdef v.ibv_wc wc
new file mode 100644
@@ -0,0 +1,295 @@
+# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
+# Copyright (c) 2019, Mellanox Technologies. All rights reserved.
+from pyverbs.base import PyverbsRDMAErrno
+cimport pyverbs.libibverbs_enums as e
+from pyverbs.device cimport Context
+
+cdef class CompChannel(PyverbsCM):
+ """
+ A completion channel is a file descriptor used to deliver completion
+ notifications to a userspace process. When a completion event is generated
+ for a CQ, the event is delivered via the completion channel attached to the
+ CQ.
+ """
+ def __cinit__(self, Context context not None):
+ """
+ Initializes a completion channel object on the given device.
+ :param context: The device's context to use
+ :return: A CompChannel object on success
+ """
+ self.cc = v.ibv_create_comp_channel(context.context)
+ if self.cc == NULL:
+ raise PyverbsRDMAErrno('Failed to create a completion channel')
+ self.context = context
+ context.add_ref(self)
+ self.logger.debug('Created a Completion Channel')
+
+ def __dealloc__(self):
+ self.close()
+
+ cpdef close(self):
+ self.logger.debug('Closing completion channel')
+ if self.cc != NULL:
+ rc = v.ibv_destroy_comp_channel(self.cc)
+ if rc != 0:
+ raise PyverbsRDMAErrno('Failed to destroy a completion channel')
+ self.cc = NULL
+
+ def get_cq_event(self, CQ expected_cq):
+ """
+ Waits for the next completion event in the completion event channel
+ :param expected_cq: The CQ that got the event
+ :return: None
+ """
+ cdef v.ibv_cq *cq
+ cdef void *ctx
+
+ rc = v.ibv_get_cq_event(self.cc, &cq, &ctx)
+ if rc != 0:
+ raise PyverbsRDMAErrno('Failed to get CQ event')
+ if cq != expected_cq.cq:
+ raise PyverbsRDMAErrno('Received event on an unexpected CQ')
+
+
+cdef class CQ(PyverbsCM):
+ """
+ A Completion Queue is the notification mechanism for work request
+ completions. A CQ can have 0 or more associated QPs.
+ """
+ def __cinit__(self, Context context not None, cqe, cq_context,
+ CompChannel channel, comp_vector):
+ """
+ Initializes a CQ object with the given parameters.
+ :param context: The device's context on which to open the CQ
+ :param cqe: CQ's capacity
+ :param cq_context: User context's pointer
+ :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
+ :return: The newly created CQ
+ """
+ self.cq = v.ibv_create_cq(context.context, cqe, <void*>cq_context,
+ NULL, comp_vector)
+ if self.cq == NULL:
+ raise PyverbsRDMAErrno('Failed to create a CQ')
+ self.context = context
+ context.add_ref(self)
+ self.logger.debug('Created a CQ')
+
+ def __dealloc__(self):
+ self.close()
+
+ cpdef close(self):
+ self.logger.debug('Closing CQ')
+ if self.cq != NULL:
+ rc = v.ibv_destroy_cq(self.cq)
+ if rc != 0:
+ raise PyverbsRDMAErrno('Failed to close CQ')
+ self.cq = NULL
+ self.context = None
+
+ def poll(self, num_entries=1):
+ """
+ Polls the CQ for completions.
+ :param num_entries: number of completions to pull
+ :return: (npolled, wcs): The number of polled completions and an array
+ of the polled completions
+ """
+ cdef v.ibv_wc wc
+ wcs = []
+ npolled = 0
+
+ while npolled < num_entries:
+ rc = v.ibv_poll_cq(self.cq, 1, &wc)
+ if rc < 0:
+ raise PyverbsRDMAErrno('Failed to poll CQ')
+ if rc == 0:
+ break;
+ npolled += 1
+ wcs.append(WC(wr_id=wc.wr_id, status=wc.status, opcode=wc.opcode,
+ vendor_err=wc.vendor_err, byte_len=wc.byte_len,
+ qp_num=wc.qp_num, src_qp=wc.src_qp,
+ imm_data=wc.imm_data, wc_flags=wc.wc_flags,
+ pkey_index=wc.pkey_index, slid=wc.slid, sl=wc.sl,
+ dlid_path_bits=wc.dlid_path_bits))
+ return npolled, wcs
+
+ @property
+ def _cq(self):
+ return <object>self.cq
+
+ def __str__(self):
+ print_format = '{:22}: {:<20}\n'
+ return '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,
+ slid=0, sl=0, dlid_path_bits=0):
+ self.wr_id = wr_id
+ self.status = status
+ self.opcode = opcode
+ self.vendor_err = vendor_err
+ self.byte_len = byte_len
+ self.qp_num = qp_num
+ self.src_qp = src_qp
+ self.wc_flags = wc_flags
+ self.pkey_index = pkey_index
+ self.slid = slid
+ self.sl = sl
+ self.dlid_path_bits = dlid_path_bits
+
+ @property
+ def wr_id(self):
+ return self.wc.wr_id
+ @wr_id.setter
+ def wr_id(self, val):
+ self.wc.wr_id = val
+
+ @property
+ def status(self):
+ return self.wc.status
+ @status.setter
+ def status(self, val):
+ self.wc.status = val
+
+ @property
+ def opcode(self):
+ return self.wc.opcode
+ @opcode.setter
+ def opcode(self, val):
+ self.wc.opcode = val
+
+ @property
+ def vendor_err(self):
+ return self.wc.vendor_err
+ @vendor_err.setter
+ def vendor_err(self, val):
+ self.wc.vendor_err = val
+
+ @property
+ def byte_len(self):
+ return self.wc.byte_len
+ @byte_len.setter
+ def byte_len(self, val):
+ self.wc.byte_len = val
+
+ @property
+ def qp_num(self):
+ return self.wc.qp_num
+ @qp_num.setter
+ def qp_num(self, val):
+ self.wc.qp_num = val
+
+ @property
+ def src_qp(self):
+ return self.wc.src_qp
+ @src_qp.setter
+ def src_qp(self, val):
+ self.wc.src_qp = val
+
+ @property
+ def wc_flags(self):
+ return self.wc.wc_flags
+ @wc_flags.setter
+ def wc_flags(self, val):
+ self.wc.wc_flags = val
+
+ @property
+ def pkey_index(self):
+ return self.wc.pkey_index
+ @pkey_index.setter
+ def pkey_index(self, val):
+ self.wc.pkey_index = val
+
+ @property
+ def slid(self):
+ return self.wc.slid
+ @slid.setter
+ def slid(self, val):
+ self.wc.slid = val
+
+ @property
+ def sl(self):
+ return self.wc.sl
+ @sl.setter
+ def sl(self, val):
+ self.wc.sl = val
+
+ @property
+ def dlid_path_bits(self):
+ return self.wc.dlid_path_bits
+ @dlid_path_bits.setter
+ def dlid_path_bits(self, val):
+ self.wc.dlid_path_bits = val
+
+ def __str__(self):
+ print_format = '{:22}: {:<20}\n'
+ return print_format.format('WR ID', self.wr_id) +\
+ print_format.format('status', cqe_status_to_str(self.status)) +\
+ print_format.format('opcode', cqe_opcode_to_str(self.opcode)) +\
+ print_format.format('vendor error', self.vendor_err) +\
+ print_format.format('byte length', self.byte_len) +\
+ print_format.format('QP num', self.qp_num) +\
+ print_format.format('source QP', self.src_qp) +\
+ print_format.format('WC flags', cqe_flags_to_str(self.wc_flags)) +\
+ print_format.format('pkey index', self.pkey_index) +\
+ print_format.format('slid', self.slid) +\
+ print_format.format('sl', self.sl) +\
+ print_format.format('dlid path bits', self.dlid_path_bits)
+
+
+def cqe_status_to_str(status):
+ try:
+ return {e.IBV_WC_SUCCESS: "success",
+ e.IBV_WC_LOC_LEN_ERR: "local length error",
+ e.IBV_WC_LOC_QP_OP_ERR: "local QP op error",
+ e.IBV_WC_LOC_EEC_OP_ERR: "local EEC op error",
+ e.IBV_WC_LOC_PROT_ERR: "local protection error",
+ e.IBV_WC_WR_FLUSH_ERR: "WR flush error",
+ e.IBV_WC_MW_BIND_ERR: "memory window bind error",
+ e.IBV_WC_BAD_RESP_ERR: "bad response error",
+ e.IBV_WC_LOC_ACCESS_ERR: "local access error",
+ e.IBV_WC_REM_INV_REQ_ERR: "remote invalidate request error",
+ e.IBV_WC_REM_ACCESS_ERR: "remote access error",
+ e.IBV_WC_REM_OP_ERR: "remote op error",
+ e.IBV_WC_RETRY_EXC_ERR: "retry exceeded error",
+ e.IBV_WC_RNR_RETRY_EXC_ERR: "RNR retry exceeded",
+ e.IBV_WC_LOC_RDD_VIOL_ERR: "local RDD violation error",
+ e.IBV_WC_REM_INV_RD_REQ_ERR: "remote invalidate RD request error",
+ e.IBV_WC_REM_ABORT_ERR: "remote abort error",
+ e.IBV_WC_INV_EECN_ERR: "invalidate EECN error",
+ e.IBV_WC_INV_EEC_STATE_ERR: "invalidate EEC state error",
+ e.IBV_WC_FATAL_ERR: "WC fatal error",
+ e.IBV_WC_RESP_TIMEOUT_ERR: "response timeout error",
+ e.IBV_WC_GENERAL_ERR: "general error"}[status]
+ except KeyError:
+ return "Unknown CQE status"
+
+def cqe_opcode_to_str(opcode):
+ try:
+ return {0x0: "Send", 0x1:"RDMA write", 0x2: "RDMA read",
+ 0x3: "Compare and swap", 0x4: "Fetch and add",
+ 0x5: "Bind Memory window", 0x6: "Local invalidate",
+ 0x7: "TSO", 0x80: "Receive",
+ 0x81: "Receive RDMA with immediate",
+ 0x82: "Tag matching - add", 0x83: "Tag matching - delete",
+ 0x84: "Tag matching - sync", 0x85: "Tag matching - receive",
+ 0x86: "Tag matching - no tag"}[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"}
+ flags_str = ""
+ for f in cqe_flags:
+ if flags & f:
+ flags_str += cqe_flags[f]
+ flags_str += " "
+ return flags_str
@@ -11,6 +11,8 @@ cdef class Context(PyverbsCM):
cdef add_ref(self, obj)
cdef object pds
cdef object dms
+ cdef object ccs
+ cdef object cqs
cdef class DeviceAttr(PyverbsObject):
cdef v.ibv_device_attr dev_attr
@@ -11,6 +11,7 @@ import weakref
from .pyverbs_error import PyverbsRDMAError, PyverbsError
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
@@ -85,6 +86,8 @@ cdef class Context(PyverbsCM):
self.pds = weakref.WeakSet()
self.dms = weakref.WeakSet()
+ self.ccs = weakref.WeakSet()
+ self.cqs = weakref.WeakSet()
dev_name = kwargs.get('name')
@@ -121,7 +124,7 @@ cdef class Context(PyverbsCM):
cpdef close(self):
self.logger.debug('Closing Context')
- self.close_weakrefs([self.dms, self.pds])
+ self.close_weakrefs([self.ccs, self.cqs, self.dms, self.pds])
if self.context != NULL:
rc = v.ibv_close_device(self.context)
if rc != 0:
@@ -129,6 +132,10 @@ cdef class Context(PyverbsCM):
format(dev=self.device.name), errno)
self.context = NULL
+ @property
+ def num_comp_vectors(self):
+ return self.context.num_comp_vectors
+
def query_device(self):
"""
Queries the device's attributes.
@@ -183,6 +190,10 @@ cdef class Context(PyverbsCM):
self.pds.add(obj)
elif isinstance(obj, DM):
self.dms.add(obj)
+ elif isinstance(obj, CompChannel):
+ self.ccs.add(obj)
+ elif isinstance(obj, CQ):
+ self.cqs.add(obj)
else:
raise PyverbsError('Unrecognized object type')
@@ -21,6 +21,7 @@ cdef extern from 'infiniband/verbs.h':
cdef struct ibv_context:
ibv_device *device
+ int num_comp_vectors
cdef struct ibv_device_attr:
char *fw_ver
@@ -172,6 +173,33 @@ cdef extern from 'infiniband/verbs.h':
unsigned char flags
unsigned short port_cap_flags2
+ cdef struct ibv_comp_channel:
+ ibv_context *context
+ unsigned int fd
+ unsigned int refcnt
+
+ cdef struct ibv_cq:
+ ibv_context *context
+ ibv_comp_channel *channel
+ void *cq_context
+ int handle
+ int cqe
+
+ cdef struct ibv_wc:
+ unsigned long wr_id
+ ibv_wc_status status
+ ibv_wc_opcode opcode
+ unsigned int vendor_err
+ unsigned int byte_len
+ unsigned int qp_num
+ unsigned int imm_data
+ unsigned int src_qp
+ int wc_flags
+ unsigned int pkey_index
+ unsigned int slid
+ unsigned int sl
+ unsigned int dlid_path_bits
+
ibv_device **ibv_get_device_list(int *n)
void ibv_free_device_list(ibv_device **list)
ibv_context *ibv_open_device(ibv_device *device)
@@ -199,3 +227,11 @@ cdef extern from 'infiniband/verbs.h':
size_t length)
int ibv_query_port(ibv_context *context, uint8_t port_num,
ibv_port_attr *port_attr)
+ ibv_comp_channel *ibv_create_comp_channel(ibv_context *context)
+ int ibv_destroy_comp_channel(ibv_comp_channel *channel)
+ int ibv_get_cq_event(ibv_comp_channel *channel, ibv_cq **cq,
+ void **cq_context)
+ ibv_cq *ibv_create_cq(ibv_context *context, int cqe, void *cq_context,
+ 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)