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
