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.
