diff --git a/drivers/common/mlx5/mlx5_devx_cmds.c b/drivers/common/mlx5/mlx5_devx_cmds.c
index ef7d70c..f843606 100644
--- a/drivers/common/mlx5/mlx5_devx_cmds.c
+++ b/drivers/common/mlx5/mlx5_devx_cmds.c
@@ -377,24 +377,18 @@ struct mlx5_devx_obj *
 		vdpa_attr->max_num_virtio_queues =
 			MLX5_GET(virtio_emulation_cap, hcattr,
 				 max_num_virtio_queues);
-		vdpa_attr->umem_1_buffer_param_a =
-			MLX5_GET(virtio_emulation_cap, hcattr,
-				 umem_1_buffer_param_a);
-		vdpa_attr->umem_1_buffer_param_b =
-			MLX5_GET(virtio_emulation_cap, hcattr,
-				 umem_1_buffer_param_b);
-		vdpa_attr->umem_2_buffer_param_a =
-			MLX5_GET(virtio_emulation_cap, hcattr,
-				 umem_2_buffer_param_a);
-		vdpa_attr->umem_2_buffer_param_b =
-			MLX5_GET(virtio_emulation_cap, hcattr,
-				 umem_2_buffer_param_a);
-		vdpa_attr->umem_3_buffer_param_a =
-			MLX5_GET(virtio_emulation_cap, hcattr,
-				 umem_3_buffer_param_a);
-		vdpa_attr->umem_3_buffer_param_b =
-			MLX5_GET(virtio_emulation_cap, hcattr,
-				 umem_3_buffer_param_b);
+		vdpa_attr->umems[0].a = MLX5_GET(virtio_emulation_cap, hcattr,
+						 umem_1_buffer_param_a);
+		vdpa_attr->umems[0].b = MLX5_GET(virtio_emulation_cap, hcattr,
+						 umem_1_buffer_param_b);
+		vdpa_attr->umems[1].a = MLX5_GET(virtio_emulation_cap, hcattr,
+						 umem_2_buffer_param_a);
+		vdpa_attr->umems[1].b = MLX5_GET(virtio_emulation_cap, hcattr,
+						 umem_2_buffer_param_b);
+		vdpa_attr->umems[2].a = MLX5_GET(virtio_emulation_cap, hcattr,
+						 umem_3_buffer_param_a);
+		vdpa_attr->umems[2].b = MLX5_GET(virtio_emulation_cap, hcattr,
+						 umem_3_buffer_param_b);
 	}
 }
 
@@ -1148,3 +1142,172 @@ struct mlx5_devx_obj *
 	cq_obj->id = MLX5_GET(create_cq_out, out, cqn);
 	return cq_obj;
 }
+
+/**
+ * Create VIRTQ using DevX API.
+ *
+ * @param[in] ctx
+ *   ibv_context returned from mlx5dv_open_device.
+ * @param [in] attr
+ *   Pointer to VIRTQ attributes structure.
+ *
+ * @return
+ *   The DevX object created, NULL otherwise and rte_errno is set.
+ */
+struct mlx5_devx_obj *
+mlx5_devx_cmd_create_virtq(struct ibv_context *ctx,
+			   struct mlx5_devx_virtq_attr *attr)
+{
+	uint32_t in[MLX5_ST_SZ_DW(create_virtq_in)] = {0};
+	uint32_t out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {0};
+	struct mlx5_devx_obj *virtq_obj = rte_zmalloc(__func__,
+						     sizeof(*virtq_obj), 0);
+	void *virtq = MLX5_ADDR_OF(create_virtq_in, in, virtq);
+	void *hdr = MLX5_ADDR_OF(create_virtq_in, in, hdr);
+	void *virtctx = MLX5_ADDR_OF(virtio_net_q, virtq, virtio_q_context);
+
+	if (!virtq_obj) {
+		DRV_LOG(ERR, "Failed to allocate virtq data.");
+		rte_errno = ENOMEM;
+		return NULL;
+	}
+	MLX5_SET(general_obj_in_cmd_hdr, hdr, opcode,
+		 MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
+	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_type,
+		 MLX5_GENERAL_OBJ_TYPE_VIRTQ);
+	MLX5_SET16(virtio_net_q, virtq, hw_available_index,
+		   attr->hw_available_index);
+	MLX5_SET16(virtio_net_q, virtq, hw_used_index, attr->hw_used_index);
+	MLX5_SET16(virtio_net_q, virtq, tso_ipv4, attr->tso_ipv4);
+	MLX5_SET16(virtio_net_q, virtq, tso_ipv6, attr->tso_ipv6);
+	MLX5_SET16(virtio_net_q, virtq, tx_csum, attr->tx_csum);
+	MLX5_SET16(virtio_net_q, virtq, rx_csum, attr->rx_csum);
+	MLX5_SET16(virtio_q, virtctx, virtio_version_1_0,
+		   attr->virtio_version_1_0);
+	MLX5_SET16(virtio_q, virtctx, event_mode, attr->event_mode);
+	MLX5_SET(virtio_q, virtctx, event_cqn_or_msix, attr->cq_id);
+	MLX5_SET64(virtio_q, virtctx, desc_addr, attr->desc_addr);
+	MLX5_SET64(virtio_q, virtctx, used_addr, attr->used_addr);
+	MLX5_SET64(virtio_q, virtctx, available_addr, attr->available_addr);
+	MLX5_SET16(virtio_q, virtctx, queue_index, attr->queue_index);
+	MLX5_SET16(virtio_q, virtctx, queue_size, attr->q_size);
+	MLX5_SET(virtio_q, virtctx, virtio_q_mkey, attr->mkey);
+	MLX5_SET(virtio_q, virtctx, umem_1_id, attr->umems[0].id);
+	MLX5_SET(virtio_q, virtctx, umem_1_size, attr->umems[0].size);
+	MLX5_SET64(virtio_q, virtctx, umem_1_offset, attr->umems[0].offset);
+	MLX5_SET(virtio_q, virtctx, umem_2_id, attr->umems[1].id);
+	MLX5_SET(virtio_q, virtctx, umem_2_size, attr->umems[1].size);
+	MLX5_SET64(virtio_q, virtctx, umem_2_offset, attr->umems[1].offset);
+	MLX5_SET(virtio_q, virtctx, umem_3_id, attr->umems[2].id);
+	MLX5_SET(virtio_q, virtctx, umem_3_size, attr->umems[2].size);
+	MLX5_SET64(virtio_q, virtctx, umem_3_offset, attr->umems[2].offset);
+	MLX5_SET(virtio_net_q, virtq, tisn_or_qpn, attr->tis_id);
+	virtq_obj->obj = mlx5_glue->devx_obj_create(ctx, in, sizeof(in), out,
+						    sizeof(out));
+	if (!virtq_obj->obj) {
+		rte_errno = errno;
+		DRV_LOG(ERR, "Failed to create VIRTQ Obj using DevX.");
+		rte_free(virtq_obj);
+		return NULL;
+	}
+	virtq_obj->id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
+	return virtq_obj;
+}
+
+/**
+ * Modify VIRTQ using DevX API.
+ *
+ * @param[in] virtq_obj
+ *   Pointer to virtq object structure.
+ * @param [in] attr
+ *   Pointer to modify virtq attributes structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_devx_cmd_modify_virtq(struct mlx5_devx_obj *virtq_obj,
+			   struct mlx5_devx_virtq_attr *attr)
+{
+	uint32_t in[MLX5_ST_SZ_DW(create_virtq_in)] = {0};
+	uint32_t out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {0};
+	void *virtq = MLX5_ADDR_OF(create_virtq_in, in, virtq);
+	void *hdr = MLX5_ADDR_OF(create_virtq_in, in, hdr);
+	void *virtctx = MLX5_ADDR_OF(virtio_net_q, virtq, virtio_q_context);
+	int ret;
+
+	MLX5_SET(general_obj_in_cmd_hdr, hdr, opcode,
+		 MLX5_CMD_OP_MODIFY_GENERAL_OBJECT);
+	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_type,
+		 MLX5_GENERAL_OBJ_TYPE_VIRTQ);
+	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_id, virtq_obj->id);
+	MLX5_SET64(virtio_net_q, virtq, modify_field_select, attr->type);
+	MLX5_SET16(virtio_q, virtctx, queue_index, attr->queue_index);
+	switch (attr->type) {
+	case MLX5_VIRTQ_MODIFY_TYPE_STATE:
+		MLX5_SET16(virtio_net_q, virtq, state, attr->state);
+		break;
+	case MLX5_VIRTQ_MODIFY_TYPE_DIRTY_BITMAP_PARAMS:
+		MLX5_SET(virtio_net_q, virtq, dirty_bitmap_mkey,
+			 attr->dirty_bitmap_mkey);
+		MLX5_SET64(virtio_net_q, virtq, dirty_bitmap_addr,
+			 attr->dirty_bitmap_addr);
+		MLX5_SET(virtio_net_q, virtq, dirty_bitmap_size,
+			 attr->dirty_bitmap_size);
+		break;
+	case MLX5_VIRTQ_MODIFY_TYPE_DIRTY_BITMAP_DUMP_ENABLE:
+		MLX5_SET(virtio_net_q, virtq, dirty_bitmap_dump_enable,
+			 attr->dirty_bitmap_dump_enable);
+		break;
+	default:
+		rte_errno = EINVAL;
+		return -rte_errno;
+	}
+	ret = mlx5_glue->devx_obj_modify(virtq_obj->obj, in, sizeof(in),
+					 out, sizeof(out));
+	if (ret) {
+		DRV_LOG(ERR, "Failed to modify VIRTQ using DevX.");
+		rte_errno = errno;
+		return -errno;
+	}
+	return ret;
+}
+
+/**
+ * Query VIRTQ using DevX API.
+ *
+ * @param[in] virtq_obj
+ *   Pointer to virtq object structure.
+ * @param [in/out] attr
+ *   Pointer to virtq attributes structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_devx_cmd_query_virtq(struct mlx5_devx_obj *virtq_obj,
+			   struct mlx5_devx_virtq_attr *attr)
+{
+	uint32_t in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {0};
+	uint32_t out[MLX5_ST_SZ_DW(query_virtq_out)] = {0};
+	void *hdr = MLX5_ADDR_OF(query_virtq_out, in, hdr);
+	void *virtq = MLX5_ADDR_OF(query_virtq_out, out, virtq);
+	int ret;
+
+	MLX5_SET(general_obj_in_cmd_hdr, hdr, opcode,
+		 MLX5_CMD_OP_QUERY_GENERAL_OBJECT);
+	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_type,
+		 MLX5_GENERAL_OBJ_TYPE_VIRTQ);
+	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_id, virtq_obj->id);
+	ret = mlx5_glue->devx_obj_query(virtq_obj->obj, in, sizeof(in),
+					 out, sizeof(out));
+	if (ret) {
+		DRV_LOG(ERR, "Failed to modify VIRTQ using DevX.");
+		rte_errno = errno;
+		return -errno;
+	}
+	attr->hw_available_index = MLX5_GET16(virtio_net_q, virtq,
+					      hw_available_index);
+	attr->hw_used_index = MLX5_GET16(virtio_net_q, virtq, hw_used_index);
+	return ret;
+}
diff --git a/drivers/common/mlx5/mlx5_devx_cmds.h b/drivers/common/mlx5/mlx5_devx_cmds.h
index 7b50861..63c84f8 100644
--- a/drivers/common/mlx5/mlx5_devx_cmds.h
+++ b/drivers/common/mlx5/mlx5_devx_cmds.h
@@ -56,12 +56,10 @@ struct mlx5_hca_vdpa_attr {
 	uint32_t log_doorbell_stride:5;
 	uint32_t log_doorbell_bar_size:5;
 	uint32_t max_num_virtio_queues;
-	uint32_t umem_1_buffer_param_a;
-	uint32_t umem_1_buffer_param_b;
-	uint32_t umem_2_buffer_param_a;
-	uint32_t umem_2_buffer_param_b;
-	uint32_t umem_3_buffer_param_a;
-	uint32_t umem_3_buffer_param_b;
+	struct {
+		uint32_t a;
+		uint32_t b;
+	} umems[3];
 	uint64_t doorbell_bar_offset;
 };
 
@@ -241,6 +239,37 @@ struct mlx5_devx_cq_attr {
 	uint64_t db_addr;
 };
 
+/* Virtq attributes structure, used by VIRTQ operations. */
+struct mlx5_devx_virtq_attr {
+	uint16_t hw_available_index;
+	uint16_t hw_used_index;
+	uint16_t q_size;
+	uint32_t virtio_version_1_0:1;
+	uint32_t tso_ipv4:1;
+	uint32_t tso_ipv6:1;
+	uint32_t tx_csum:1;
+	uint32_t rx_csum:1;
+	uint32_t event_mode:3;
+	uint32_t state:4;
+	uint32_t dirty_bitmap_dump_enable:1;
+	uint32_t dirty_bitmap_mkey;
+	uint32_t dirty_bitmap_size;
+	uint32_t mkey;
+	uint32_t cq_id;
+	uint32_t queue_index;
+	uint32_t tis_id;
+	uint64_t dirty_bitmap_addr;
+	uint64_t type;
+	uint64_t desc_addr;
+	uint64_t used_addr;
+	uint64_t available_addr;
+	struct {
+		uint32_t id;
+		uint32_t size;
+		uint64_t offset;
+	} umems[3];
+};
+
 /* mlx5_devx_cmds.c */
 
 struct mlx5_devx_obj *mlx5_devx_cmd_flow_counter_alloc(struct ibv_context *ctx,
@@ -279,4 +308,11 @@ int mlx5_devx_cmd_flow_dump(void *fdb_domain, void *rx_domain, void *tx_domain,
 			    FILE *file);
 struct mlx5_devx_obj *mlx5_devx_cmd_create_cq(struct ibv_context *ctx,
 					      struct mlx5_devx_cq_attr *attr);
+struct mlx5_devx_obj *mlx5_devx_cmd_create_virtq(struct ibv_context *ctx,
+					     struct mlx5_devx_virtq_attr *attr);
+int mlx5_devx_cmd_modify_virtq(struct mlx5_devx_obj *virtq_obj,
+			       struct mlx5_devx_virtq_attr *attr);
+int mlx5_devx_cmd_query_virtq(struct mlx5_devx_obj *virtq_obj,
+			      struct mlx5_devx_virtq_attr *attr);
+
 #endif /* RTE_PMD_MLX5_DEVX_CMDS_H_ */
diff --git a/drivers/common/mlx5/mlx5_prm.h b/drivers/common/mlx5/mlx5_prm.h
index 0206a8e..6db89bb 100644
--- a/drivers/common/mlx5/mlx5_prm.h
+++ b/drivers/common/mlx5/mlx5_prm.h
@@ -527,6 +527,8 @@ struct mlx5_modification_cmd {
 #define __mlx5_16_bit_off(typ, fld) (16 - __mlx5_bit_sz(typ, fld) - \
 				    (__mlx5_bit_off(typ, fld) & 0xf))
 #define __mlx5_mask16(typ, fld) ((u16)((1ull << __mlx5_bit_sz(typ, fld)) - 1))
+#define __mlx5_16_mask(typ, fld) (__mlx5_mask16(typ, fld) << \
+				  __mlx5_16_bit_off(typ, fld))
 #define MLX5_ST_SZ_BYTES(typ) (sizeof(struct mlx5_ifc_##typ##_bits) / 8)
 #define MLX5_ST_SZ_DW(typ) (sizeof(struct mlx5_ifc_##typ##_bits) / 32)
 #define MLX5_BYTE_OFF(typ, fld) (__mlx5_bit_off(typ, fld) / 8)
@@ -551,6 +553,17 @@ struct mlx5_modification_cmd {
 			rte_cpu_to_be_64(v); \
 	} while (0)
 
+#define MLX5_SET16(typ, p, fld, v) \
+	do { \
+		u16 _v = v; \
+		*((__be16 *)(p) + __mlx5_16_off(typ, fld)) = \
+		rte_cpu_to_be_16((rte_be_to_cpu_16(*((__be16 *)(p) + \
+				  __mlx5_16_off(typ, fld))) & \
+				  (~__mlx5_16_mask(typ, fld))) | \
+				 (((_v) & __mlx5_mask16(typ, fld)) << \
+				  __mlx5_16_bit_off(typ, fld))); \
+	} while (0)
+
 #define MLX5_GET(typ, p, fld) \
 	((rte_be_to_cpu_32(*((__be32 *)(p) +\
 	__mlx5_dw_off(typ, fld))) >> __mlx5_dw_bit_off(typ, fld)) & \
@@ -723,6 +736,9 @@ enum {
 	MLX5_CMD_OP_CREATE_RQT = 0x916,
 	MLX5_CMD_OP_ALLOC_FLOW_COUNTER = 0x939,
 	MLX5_CMD_OP_QUERY_FLOW_COUNTER = 0x93b,
+	MLX5_CMD_OP_CREATE_GENERAL_OBJECT = 0xa00,
+	MLX5_CMD_OP_MODIFY_GENERAL_OBJECT = 0xa01,
+	MLX5_CMD_OP_QUERY_GENERAL_OBJECT = 0xa02,
 };
 
 enum {
@@ -1689,6 +1705,11 @@ struct mlx5_ifc_create_tir_in_bits {
 	struct mlx5_ifc_tirc_bits ctx;
 };
 
+enum {
+	MLX5_INLINE_Q_TYPE_RQ = 0x0,
+	MLX5_INLINE_Q_TYPE_VIRTQ = 0x1,
+};
+
 struct mlx5_ifc_rq_num_bits {
 	u8 reserved_at_0[0x8];
 	u8 rq_num[0x18];
@@ -1915,6 +1936,102 @@ struct mlx5_ifc_create_cq_in_bits {
 	u8 pas[];
 };
 
+enum {
+	MLX5_GENERAL_OBJ_TYPE_VIRTQ = 0x000d,
+};
+
+struct mlx5_ifc_general_obj_in_cmd_hdr_bits {
+	u8 opcode[0x10];
+	u8 reserved_at_10[0x20];
+	u8 obj_type[0x10];
+	u8 obj_id[0x20];
+	u8 reserved_at_60[0x20];
+};
+
+struct mlx5_ifc_general_obj_out_cmd_hdr_bits {
+	u8 status[0x8];
+	u8 reserved_at_8[0x18];
+	u8 syndrome[0x20];
+	u8 obj_id[0x20];
+	u8 reserved_at_60[0x20];
+};
+
+enum {
+	MLX5_VIRTQ_STATE_INIT = 0,
+	MLX5_VIRTQ_STATE_RDY = 1,
+	MLX5_VIRTQ_STATE_SUSPEND = 2,
+	MLX5_VIRTQ_STATE_ERROR = 3,
+};
+
+enum {
+	MLX5_VIRTQ_MODIFY_TYPE_STATE = (1UL << 0),
+	MLX5_VIRTQ_MODIFY_TYPE_DIRTY_BITMAP_PARAMS = (1UL << 3),
+	MLX5_VIRTQ_MODIFY_TYPE_DIRTY_BITMAP_DUMP_ENABLE = (1UL << 4),
+};
+
+struct mlx5_ifc_virtio_q_bits {
+	u8 virtio_q_type[0x8];
+	u8 reserved_at_8[0x5];
+	u8 event_mode[0x3];
+	u8 queue_index[0x10];
+	u8 full_emulation[0x1];
+	u8 virtio_version_1_0[0x1];
+	u8 reserved_at_22[0x2];
+	u8 offload_type[0x4];
+	u8 event_cqn_or_msix[0x18];
+	u8 doorbell_stride_idx[0x10];
+	u8 queue_size[0x10];
+	u8 device_emulation_id[0x20];
+	u8 desc_addr[0x40];
+	u8 used_addr[0x40];
+	u8 available_addr[0x40];
+	u8 virtio_q_mkey[0x20];
+	u8 reserved_at_160[0x20];
+	u8 umem_1_id[0x20];
+	u8 umem_1_size[0x20];
+	u8 umem_1_offset[0x40];
+	u8 umem_2_id[0x20];
+	u8 umem_2_size[0x20];
+	u8 umem_2_offset[0x40];
+	u8 umem_3_id[0x20];
+	u8 umem_3_size[0x20];
+	u8 umem_3_offset[0x40];
+	u8 reserved_at_300[0x100];
+};
+
+struct mlx5_ifc_virtio_net_q_bits {
+	u8 modify_field_select[0x40];
+	u8 reserved_at_40[0x40];
+	u8 tso_ipv4[0x1];
+	u8 tso_ipv6[0x1];
+	u8 tx_csum[0x1];
+	u8 rx_csum[0x1];
+	u8 reserved_at_84[0x6];
+	u8 dirty_bitmap_dump_enable[0x1];
+	u8 vhost_log_page[0x5];
+	u8 reserved_at_90[0xc];
+	u8 state[0x4];
+	u8 error_type[0x8];
+	u8 tisn_or_qpn[0x18];
+	u8 dirty_bitmap_mkey[0x20];
+	u8 dirty_bitmap_size[0x20];
+	u8 dirty_bitmap_addr[0x40];
+	u8 hw_available_index[0x10];
+	u8 hw_used_index[0x10];
+	u8 reserved_at_160[0xa0];
+	struct mlx5_ifc_virtio_q_bits virtio_q_context;
+};
+
+struct mlx5_ifc_create_virtq_in_bits {
+	struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
+	struct mlx5_ifc_virtio_net_q_bits virtq;
+};
+
+struct mlx5_ifc_query_virtq_out_bits {
+	struct mlx5_ifc_general_obj_in_cmd_hdr_bits hdr;
+	struct mlx5_ifc_virtio_net_q_bits virtq;
+};
+
 /* CQE format mask. */
 #define MLX5E_CQE_FORMAT_MASK 0xc
 
diff --git a/drivers/common/mlx5/rte_common_mlx5_version.map b/drivers/common/mlx5/rte_common_mlx5_version.map
index c6a203d..f3082ce 100644
--- a/drivers/common/mlx5/rte_common_mlx5_version.map
+++ b/drivers/common/mlx5/rte_common_mlx5_version.map
@@ -8,6 +8,7 @@ DPDK_20.02 {
 	mlx5_devx_cmd_create_tir;
 	mlx5_devx_cmd_create_td;
 	mlx5_devx_cmd_create_tis;
+	mlx5_devx_cmd_create_virtq;
 	mlx5_devx_cmd_destroy;
 	mlx5_devx_cmd_flow_counter_alloc;
 	mlx5_devx_cmd_flow_counter_query;
@@ -15,8 +16,10 @@ DPDK_20.02 {
 	mlx5_devx_cmd_mkey_create;
 	mlx5_devx_cmd_modify_rq;
 	mlx5_devx_cmd_modify_sq;
+	mlx5_devx_cmd_modify_virtq;
 	mlx5_devx_cmd_qp_query_tis_td;
 	mlx5_devx_cmd_query_hca_attr;
+	mlx5_devx_cmd_query_virtq;
 	mlx5_devx_get_out_command_status;
 
 	mlx5_dev_to_pci_addr;
