diff --git a/pyverbs/CMakeLists.txt b/pyverbs/CMakeLists.txt
index c532b4c..80f6e2b 100644
--- a/pyverbs/CMakeLists.txt
+++ b/pyverbs/CMakeLists.txt
@@ -44,4 +44,5 @@ rdma_python_module(pyverbs
 if (HAVE_COHERENT_DMA)
 add_subdirectory(providers/mlx5)
 add_subdirectory(providers/efa)
+add_subdirectory(providers/hns)
 endif()
diff --git a/pyverbs/providers/hns/CMakeLists.txt b/pyverbs/providers/hns/CMakeLists.txt
new file mode 100644
index 0000000..bb60f16
--- /dev/null
+++ b/pyverbs/providers/hns/CMakeLists.txt
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
+# Copyright (c) 2021 HiSilicon Limited. All rights reserved.
+
+rdma_cython_module(pyverbs/providers/hns hns
+  hns_enums.pyx
+  hnsdv.pyx
+)
diff --git a/pyverbs/providers/hns/__init__.pxd b/pyverbs/providers/hns/__init__.pxd
new file mode 100644
index 0000000..e69de29
diff --git a/pyverbs/providers/hns/__init__.py b/pyverbs/providers/hns/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pyverbs/providers/hns/hns_enums.pyx b/pyverbs/providers/hns/hns_enums.pyx
new file mode 120000
index 0000000..33b3389
--- /dev/null
+++ b/pyverbs/providers/hns/hns_enums.pyx
@@ -0,0 +1 @@
+hnsdv_enums.pxd
\ No newline at end of file
diff --git a/pyverbs/providers/hns/hnsdv.pxd b/pyverbs/providers/hns/hnsdv.pxd
new file mode 100644
index 0000000..b23fab8
--- /dev/null
+++ b/pyverbs/providers/hns/hnsdv.pxd
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
+# Copyright (c) 2021 HiSilicon Limited. All rights reserved.
+
+#cython: language_level=3
+
+from pyverbs.base cimport PyverbsObject
+cimport pyverbs.providers.hns.libhns as dv
+from pyverbs.device cimport Context
+from pyverbs.qp cimport QP, QPEx
+
+
+cdef class HnsContext(Context):
+    cpdef close(self)
+
+cdef class HnsDVContextAttr(PyverbsObject):
+    cdef dv.hnsdv_context_attr attr
+
+cdef class HnsDVContext(PyverbsObject):
+    pass
+
+cdef class HnsDVQPInitAttr(PyverbsObject):
+    cdef dv.hnsdv_qp_init_attr attr
+
+cdef class HnsQP(QPEx):
+    pass
diff --git a/pyverbs/providers/hns/hnsdv.pyx b/pyverbs/providers/hns/hnsdv.pyx
new file mode 100644
index 0000000..4642255
--- /dev/null
+++ b/pyverbs/providers/hns/hnsdv.pyx
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
+# Copyright (c) 2021 HiSilicon Limited. All rights reserved.
+
+from libc.stdint cimport uintptr_t, uint8_t, uint16_t, uint32_t
+import logging
+
+from pyverbs.pyverbs_error import PyverbsUserError
+
+cimport pyverbs.providers.hns.hnsdv_enums as dve
+cimport pyverbs.providers.hns.libhns as dv
+
+from pyverbs.qp cimport QPInitAttrEx, QPEx
+from pyverbs.base import PyverbsRDMAErrno
+from pyverbs.base cimport close_weakrefs
+from pyverbs.pd cimport PD
+
+cdef class HnsDVContextAttr(PyverbsObject):
+    """
+    Represent hnsdv_context_attr struct. This class is used to open an hns
+    device.
+    """
+    def __init__(self, flags=0, comp_mask=0, dca_qps=1):
+        super().__init__()
+        self.attr.flags = flags
+        self.attr.comp_mask = comp_mask
+        if dca_qps > 0:
+            self.attr.comp_mask |= dve.HNSDV_CONTEXT_MASK_DCA_PRIME_QPS
+            self.attr.dca_prime_qps = dca_qps
+
+    def __str__(self):
+        print_format = '{:20}: {:<20}\n'
+        return print_format.format('flags', self.attr.flags) +\
+               print_format.format('comp_mask', self.attr.comp_mask)
+
+    @property
+    def flags(self):
+        return self.attr.flags
+    @flags.setter
+    def flags(self, val):
+        self.attr.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
+
+cdef class HnsContext(Context):
+    """
+    Represent hns context, which extends Context.
+    """
+    def __init__(self, HnsDVContextAttr attr not None, name=''):
+        """
+        Open an hns device using the given attributes
+        :param name: The RDMA device's name (used by parent class)
+        :param attr: hns-specific device attributes
+        :return: None
+        """
+        super().__init__(name=name, attr=attr)
+        if not dv.hnsdv_is_supported(self.device):
+            raise PyverbsUserError('This is not an HNS device')
+        self.context = dv.hnsdv_open_device(self.device, &attr.attr)
+        if self.context == NULL:
+            raise PyverbsRDMAErrno('Failed to open hns context on {dev}'
+                                   .format(dev=self.name))
+
+    def __dealloc__(self):
+        self.close()
+
+    cpdef close(self):
+        if self.context != NULL:
+            super(HnsContext, self).close()
+
+cdef class HnsDVQPInitAttr(PyverbsObject):
+    """
+    Represents hnsdv_qp_init_attr struct, initial attributes used for hns QP
+    creation.
+    """
+    def __init__(self, comp_mask=0, create_flags=0):
+        """
+        Initializes an HnsDVQPInitAttr object with the given user data.
+        :param comp_mask: A bitmask specifying which fields are valid
+        :param create_flags: A bitwise OR of hnsdv_qp_create_flags
+        :return: An initialized HnsDVQPInitAttr object
+        """
+        super().__init__()
+        self.attr.comp_mask = comp_mask
+        self.attr.create_flags = create_flags
+
+    def __str__(self):
+        print_format = '{:20}: {:<20}\n'
+        return print_format.format('Comp mask',
+                                   qp_comp_mask_to_str(self.attr.comp_mask)) +\
+               print_format.format('Create flags',
+                                   qp_create_flags_to_str(self.attr.create_flags))
+
+    @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 create_flags(self):
+        return self.attr.create_flags
+    @create_flags.setter
+    def create_flags(self, val):
+        self.attr.create_flags = val
+
+cdef class HnsQP(QPEx):
+    def __init__(self, Context context, QPInitAttrEx init_attr,
+                 HnsDVQPInitAttr dv_init_attr):
+        """
+        Initializes an hns QP according to the user-provided data.
+        :param context: Context object
+        :param init_attr: QPInitAttrEx object
+        :return: An initialized HnsQP
+        """
+        cdef PD pd
+
+        # Initialize the logger here as the parent's __init__ is called after
+        # the QP is allocated. Allocation can fail, which will lead to exceptions
+        # thrown during object's teardown.
+        self.logger = logging.getLogger(self.__class__.__name__)
+        if init_attr.pd is not None:
+            pd = <PD>init_attr.pd
+            pd.add_ref(self)
+        self.qp = \
+            dv.hnsdv_create_qp(context.context,
+                                &init_attr.attr,
+                                &dv_init_attr.attr if dv_init_attr is not None
+                                else NULL)
+        if self.qp == NULL:
+            raise PyverbsRDMAErrno('Failed to create HNS QP.\nQPInitAttrEx '
+                                   'attributes:\n{}\nHNSDVQPInitAttr:\n{}'.
+                                   format(init_attr, dv_init_attr))
+        super().__init__(context, init_attr)
+
+def bitmask_to_str(bits, values):
+    numeric_bits = bits
+    res = ''
+    for t in values.keys():
+        if t & bits:
+            res += values[t] + ', '
+            bits -= t
+        if bits == 0:
+            break
+    return res[:-2] + ' ({})'.format(numeric_bits) # Remove last comma and space
+
+def qp_comp_mask_to_str(flags):
+    l = {dve.HNSDV_QP_INIT_ATTR_MASK_QP_CREATE_FLAGS: 'Create flags'}
+    return bitmask_to_str(flags, l)
+
+def qp_create_flags_to_str(flags):
+    l = {dve.HNSDV_QP_CREATE_ENABLE_DCA_MODE: 'Enable DCA'}
+    return bitmask_to_str(flags, l)
diff --git a/pyverbs/providers/hns/hnsdv_enums.pxd b/pyverbs/providers/hns/hnsdv_enums.pxd
new file mode 100644
index 0000000..9fa43af
--- /dev/null
+++ b/pyverbs/providers/hns/hnsdv_enums.pxd
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
+# Copyright (c) 2021 HiSilicon Limited. All rights reserved.
+
+#cython: language_level=3
+
+cdef extern from 'infiniband/hnsdv.h':
+
+    cpdef enum hnsdv_context_attr_flags:
+        HNSDV_CONTEXT_FLAGS_DCA	= 1 << 0
+
+    cpdef enum hnsdv_context_comp_mask:
+        HNSDV_CONTEXT_MASK_DCA_PRIME_QPS	= 1 << 0
+        HNSDV_CONTEXT_MASK_DCA_UNIT_SIZE	= 1 << 1
+        HNSDV_CONTEXT_MASK_DCA_MAX_SIZE		= 1 << 2
+        HNSDV_CONTEXT_MASK_DCA_MIN_SIZE		= 1 << 3
+
+    cpdef enum hnsdv_qp_init_attr_mask:
+        HNSDV_QP_INIT_ATTR_MASK_QP_CREATE_FLAGS	= 1 << 0
+
+    cpdef enum hnsdv_qp_create_flags:
+        HNSDV_QP_CREATE_ENABLE_DCA_MODE		= 1 << 0
diff --git a/pyverbs/providers/hns/libhns.pxd b/pyverbs/providers/hns/libhns.pxd
new file mode 100644
index 0000000..c1e4ec3
--- /dev/null
+++ b/pyverbs/providers/hns/libhns.pxd
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB)
+# Copyright (c) 2021 HiSilicon Limited. All rights reserved.
+
+from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t
+from libcpp cimport bool
+
+cimport pyverbs.libibverbs as v
+
+cdef extern from 'infiniband/hnsdv.h':
+
+    cdef struct hnsdv_context_attr:
+        uint64_t flags
+        uint64_t comp_mask
+        uint32_t dca_prime_qps
+        uint32_t dca_unit_size
+        uint64_t dca_max_size
+        uint64_t dca_min_size
+
+    cdef struct hnsdv_qp_init_attr:
+        uint64_t comp_mask
+        uint32_t create_flags
+
+    bool hnsdv_is_supported(v.ibv_device *device)
+    v.ibv_context* hnsdv_open_device(v.ibv_device *device,
+                                     hnsdv_context_attr *attr)
+    v.ibv_qp *hnsdv_create_qp(v.ibv_context *context,
+                              v.ibv_qp_init_attr_ex *qp_attr,
+                              hnsdv_qp_init_attr *hns_qp_attr)
