diff --git a/libibverbs/CMakeLists.txt b/libibverbs/CMakeLists.txt
index 2b77828..73380ed 100644
--- a/libibverbs/CMakeLists.txt
+++ b/libibverbs/CMakeLists.txt
@@ -36,6 +36,7 @@ rdma_library(ibverbs "${CMAKE_CURRENT_BINARY_DIR}/libibverbs.map"
   cmd_mr.c
   cmd_mw.c
   cmd_pd.c
+  cmd_qp.c
   cmd_rwq_ind.c
   cmd_srq.c
   cmd_wq.c
diff --git a/libibverbs/cmd.c b/libibverbs/cmd.c
index d962649..9512639 100644
--- a/libibverbs/cmd.c
+++ b/libibverbs/cmd.c
@@ -543,90 +543,6 @@ int ibv_cmd_query_srq(struct ibv_srq *srq, struct ibv_srq_attr *srq_attr,
 	return 0;
 }
 
-static int create_qp_ex_common(struct verbs_qp *qp,
-			       struct ibv_qp_init_attr_ex *qp_attr,
-			       struct verbs_xrcd *vxrcd,
-			       struct ib_uverbs_create_qp *cmd)
-{
-	cmd->user_handle = (uintptr_t)qp;
-
-	if (qp_attr->comp_mask & IBV_QP_INIT_ATTR_XRCD) {
-		vxrcd = container_of(qp_attr->xrcd, struct verbs_xrcd, xrcd);
-		cmd->pd_handle	= vxrcd->handle;
-	} else {
-		if (!(qp_attr->comp_mask & IBV_QP_INIT_ATTR_PD))
-			return EINVAL;
-
-		cmd->pd_handle	= qp_attr->pd->handle;
-		if (qp_attr->comp_mask & IBV_QP_INIT_ATTR_IND_TABLE) {
-			if (cmd->max_recv_wr || cmd->max_recv_sge ||
-			    cmd->recv_cq_handle || qp_attr->srq)
-				return EINVAL;
-
-			/* send_cq is optinal */
-			if (qp_attr->cap.max_send_wr)
-				cmd->send_cq_handle = qp_attr->send_cq->handle;
-		} else {
-			cmd->send_cq_handle = qp_attr->send_cq->handle;
-
-			if (qp_attr->qp_type != IBV_QPT_XRC_SEND) {
-				cmd->recv_cq_handle = qp_attr->recv_cq->handle;
-				cmd->srq_handle = qp_attr->srq ? qp_attr->srq->handle :
-								 0;
-			}
-		}
-	}
-
-	cmd->max_send_wr     = qp_attr->cap.max_send_wr;
-	cmd->max_recv_wr     = qp_attr->cap.max_recv_wr;
-	cmd->max_send_sge    = qp_attr->cap.max_send_sge;
-	cmd->max_recv_sge    = qp_attr->cap.max_recv_sge;
-	cmd->max_inline_data = qp_attr->cap.max_inline_data;
-	cmd->sq_sig_all	     = qp_attr->sq_sig_all;
-	cmd->qp_type         = qp_attr->qp_type;
-	cmd->is_srq	     = !!qp_attr->srq;
-	cmd->reserved	     = 0;
-
-	return 0;
-}
-
-static void create_qp_handle_resp_common(struct ibv_context *context,
-					 struct verbs_qp *qp,
-					 struct ibv_qp_init_attr_ex *qp_attr,
-					 struct ib_uverbs_create_qp_resp *resp,
-					 struct verbs_xrcd *vxrcd,
-					 int vqp_sz)
-{
-	if (abi_ver > 3) {
-		qp_attr->cap.max_recv_sge    = resp->max_recv_sge;
-		qp_attr->cap.max_send_sge    = resp->max_send_sge;
-		qp_attr->cap.max_recv_wr     = resp->max_recv_wr;
-		qp_attr->cap.max_send_wr     = resp->max_send_wr;
-		qp_attr->cap.max_inline_data = resp->max_inline_data;
-	}
-
-	qp->qp.handle		= resp->qp_handle;
-	qp->qp.qp_num		= resp->qpn;
-	qp->qp.context		= context;
-	qp->qp.qp_context	= qp_attr->qp_context;
-	qp->qp.pd		= qp_attr->pd;
-	qp->qp.send_cq		= qp_attr->send_cq;
-	qp->qp.recv_cq		= qp_attr->recv_cq;
-	qp->qp.srq		= qp_attr->srq;
-	qp->qp.qp_type		= qp_attr->qp_type;
-	qp->qp.state		= IBV_QPS_RESET;
-	qp->qp.events_completed = 0;
-	pthread_mutex_init(&qp->qp.mutex, NULL);
-	pthread_cond_init(&qp->qp.cond, NULL);
-
-	qp->comp_mask = 0;
-	if (vext_field_avail(struct verbs_qp, xrcd, vqp_sz) &&
-	    (qp_attr->comp_mask & IBV_QP_INIT_ATTR_XRCD)) {
-		qp->comp_mask |= VERBS_QP_XRCD;
-		qp->xrcd = vxrcd;
-	}
-}
-
 enum {
 	CREATE_QP_EX2_SUP_CREATE_FLAGS = IBV_QP_CREATE_BLOCK_SELF_MCAST_LB |
 					 IBV_QP_CREATE_SCATTER_FCS |
@@ -635,163 +551,6 @@ enum {
 					 IBV_QP_CREATE_PCI_WRITE_END_PADDING,
 };
 
-int ibv_cmd_create_qp_ex2(struct ibv_context *context,
-			  struct verbs_qp *qp, int vqp_sz,
-			  struct ibv_qp_init_attr_ex *qp_attr,
-			  struct ibv_create_qp_ex *cmd,
-			  size_t cmd_size,
-			  struct ib_uverbs_ex_create_qp_resp *resp,
-			  size_t resp_size)
-{
-	struct verbs_xrcd *vxrcd = NULL;
-	int err;
-
-	if (!check_comp_mask(qp_attr->comp_mask,
-			     IBV_QP_INIT_ATTR_PD |
-			     IBV_QP_INIT_ATTR_XRCD |
-			     IBV_QP_INIT_ATTR_CREATE_FLAGS |
-			     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))
-		return EINVAL;
-
-	memset(&cmd->core_payload, 0, sizeof(cmd->core_payload));
-
-	err = create_qp_ex_common(qp, qp_attr, vxrcd,
-				  ibv_create_qp_ex_to_reg(cmd));
-	if (err)
-		return err;
-
-	if (qp_attr->comp_mask & IBV_QP_INIT_ATTR_CREATE_FLAGS) {
-		if (qp_attr->create_flags & ~CREATE_QP_EX2_SUP_CREATE_FLAGS)
-			return EINVAL;
-		cmd->create_flags = qp_attr->create_flags;
-
-		if (qp_attr->create_flags & IBV_QP_CREATE_SOURCE_QPN)
-			cmd->source_qpn = qp_attr->source_qpn;
-	}
-
-	if (qp_attr->comp_mask & IBV_QP_INIT_ATTR_IND_TABLE) {
-		cmd->rwq_ind_tbl_handle = qp_attr->rwq_ind_tbl->ind_tbl_handle;
-		cmd->comp_mask = IB_UVERBS_CREATE_QP_MASK_IND_TABLE;
-	}
-
-	err = execute_cmd_write_ex(context, IB_USER_VERBS_EX_CMD_CREATE_QP,
-				   cmd, cmd_size, resp, resp_size);
-	if (err)
-		return err;
-
-	create_qp_handle_resp_common(context, qp, qp_attr, &resp->base, vxrcd,
-				     vqp_sz);
-
-	return 0;
-}
-
-int ibv_cmd_create_qp_ex(struct ibv_context *context,
-			 struct verbs_qp *qp, int vqp_sz,
-			 struct ibv_qp_init_attr_ex *attr_ex,
-			 struct ibv_create_qp *cmd, size_t cmd_size,
-			 struct ib_uverbs_create_qp_resp *resp, size_t resp_size)
-{
-	struct verbs_xrcd *vxrcd = NULL;
-	int err;
-
-	if (!check_comp_mask(attr_ex->comp_mask,
-			     IBV_QP_INIT_ATTR_PD |
-			     IBV_QP_INIT_ATTR_XRCD |
-			     IBV_QP_INIT_ATTR_SEND_OPS_FLAGS))
-		return EOPNOTSUPP;
-
-	err = create_qp_ex_common(qp, attr_ex, vxrcd,
-				  &cmd->core_payload);
-	if (err)
-		return err;
-
-	err = execute_cmd_write(context, IB_USER_VERBS_CMD_CREATE_QP, cmd,
-				cmd_size, resp, resp_size);
-	if (err)
-		return err;
-
-	if (abi_ver == 4) {
-		struct ibv_create_qp_resp_v4 *resp_v4 =
-			(struct ibv_create_qp_resp_v4 *)resp;
-
-		memmove((void *)resp + sizeof *resp,
-			(void *)resp_v4 + sizeof *resp_v4,
-			resp_size - sizeof *resp);
-	} else if (abi_ver <= 3) {
-		struct ibv_create_qp_resp_v3 *resp_v3 =
-			(struct ibv_create_qp_resp_v3 *)resp;
-
-		memmove((void *)resp + sizeof *resp,
-			(void *)resp_v3 + sizeof *resp_v3,
-			resp_size - sizeof *resp);
-	}
-
-	create_qp_handle_resp_common(context, qp, attr_ex, resp, vxrcd, vqp_sz);
-
-	return 0;
-}
-
-int ibv_cmd_create_qp(struct ibv_pd *pd,
-		      struct ibv_qp *qp, struct ibv_qp_init_attr *attr,
-		      struct ibv_create_qp *cmd, size_t cmd_size,
-		      struct ib_uverbs_create_qp_resp *resp, size_t resp_size)
-{
-	int ret;
-
-	cmd->user_handle     = (uintptr_t) qp;
-	cmd->pd_handle       = pd->handle;
-	cmd->send_cq_handle  = attr->send_cq->handle;
-	cmd->recv_cq_handle  = attr->recv_cq->handle;
-	cmd->srq_handle      = attr->srq ? attr->srq->handle : 0;
-	cmd->max_send_wr     = attr->cap.max_send_wr;
-	cmd->max_recv_wr     = attr->cap.max_recv_wr;
-	cmd->max_send_sge    = attr->cap.max_send_sge;
-	cmd->max_recv_sge    = attr->cap.max_recv_sge;
-	cmd->max_inline_data = attr->cap.max_inline_data;
-	cmd->sq_sig_all	     = attr->sq_sig_all;
-	cmd->qp_type 	     = attr->qp_type;
-	cmd->is_srq 	     = !!attr->srq;
-	cmd->reserved	     = 0;
-
-	ret = execute_cmd_write(pd->context, IB_USER_VERBS_CMD_CREATE_QP, cmd,
-				cmd_size, resp, resp_size);
-	if (ret)
-		return ret;
-
-	qp->handle 		  = resp->qp_handle;
-	qp->qp_num 		  = resp->qpn;
-	qp->context		  = pd->context;
-
-	if (abi_ver > 3) {
-		attr->cap.max_recv_sge    = resp->max_recv_sge;
-		attr->cap.max_send_sge    = resp->max_send_sge;
-		attr->cap.max_recv_wr     = resp->max_recv_wr;
-		attr->cap.max_send_wr     = resp->max_send_wr;
-		attr->cap.max_inline_data = resp->max_inline_data;
-	}
-
-	if (abi_ver == 4) {
-		struct ibv_create_qp_resp_v4 *resp_v4 =
-			(struct ibv_create_qp_resp_v4 *) resp;
-
-		memmove((void *) resp + sizeof *resp,
-			(void *) resp_v4 + sizeof *resp_v4,
-			resp_size - sizeof *resp);
-	} else if (abi_ver <= 3) {
-		struct ibv_create_qp_resp_v3 *resp_v3 =
-			(struct ibv_create_qp_resp_v3 *) resp;
-
-		memmove((void *) resp + sizeof *resp,
-			(void *) resp_v3 + sizeof *resp_v3,
-			resp_size - sizeof *resp);
-	}
-
-	return 0;
-}
-
 int ibv_cmd_open_qp(struct ibv_context *context, struct verbs_qp *qp,
 		    int vqp_sz,
 		    struct ibv_qp_open_attr *attr,
@@ -1294,29 +1053,6 @@ int ibv_cmd_create_ah(struct ibv_pd *pd, struct ibv_ah *ah,
 	return 0;
 }
 
-int ibv_cmd_destroy_qp(struct ibv_qp *qp)
-{
-	struct ibv_destroy_qp req;
-	struct ib_uverbs_destroy_qp_resp resp;
-	int ret;
-
-	req.core_payload = (struct ib_uverbs_destroy_qp){
-		.qp_handle = qp->handle,
-	};
-
-	ret = execute_cmd_write(qp->context, IB_USER_VERBS_CMD_DESTROY_QP, &req,
-				sizeof(req), &resp, sizeof(resp));
-	if (verbs_is_destroy_err(&ret))
-		return ret;
-
-	pthread_mutex_lock(&qp->mutex);
-	while (qp->events_completed != resp.events_reported)
-		pthread_cond_wait(&qp->cond, &qp->mutex);
-	pthread_mutex_unlock(&qp->mutex);
-
-	return 0;
-}
-
 int ibv_cmd_attach_mcast(struct ibv_qp *qp, const union ibv_gid *gid, uint16_t lid)
 {
 	struct ibv_attach_mcast req;
diff --git a/libibverbs/cmd_qp.c b/libibverbs/cmd_qp.c
new file mode 100644
index 0000000..a11bb98
--- /dev/null
+++ b/libibverbs/cmd_qp.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2020 Mellanox Technologies, Ltd.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <infiniband/cmd_write.h>
+#include "ibverbs.h"
+
+enum {
+	CREATE_QP_EX_SUP_CREATE_FLAGS = IBV_QP_CREATE_BLOCK_SELF_MCAST_LB |
+					IBV_QP_CREATE_SCATTER_FCS |
+					IBV_QP_CREATE_CVLAN_STRIPPING |
+					IBV_QP_CREATE_SOURCE_QPN |
+					IBV_QP_CREATE_PCI_WRITE_END_PADDING
+};
+
+
+static void set_qp(struct verbs_qp *vqp,
+		   struct ibv_qp *qp_in,
+		   struct ibv_qp_init_attr_ex *attr_ex,
+		   struct verbs_xrcd *vxrcd)
+{
+	struct ibv_qp *qp = vqp ? &vqp->qp : qp_in;
+
+	qp->qp_context = attr_ex->qp_context;
+	qp->pd = attr_ex->pd;
+	qp->send_cq = attr_ex->send_cq;
+	qp->recv_cq = attr_ex->recv_cq;
+	qp->srq = attr_ex->srq;
+	qp->qp_type		= attr_ex->qp_type;
+	qp->state		= IBV_QPS_RESET;
+	qp->events_completed = 0;
+	pthread_mutex_init(&qp->mutex, NULL);
+	pthread_cond_init(&qp->cond, NULL);
+
+	if (vqp) {
+		vqp->comp_mask = 0;
+		if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_XRCD) {
+			vqp->comp_mask |= VERBS_QP_XRCD;
+			vqp->xrcd = vxrcd;
+		}
+	}
+}
+
+static int ibv_icmd_create_qp(struct ibv_context *context,
+			      struct verbs_qp *vqp,
+			      struct ibv_qp *qp_in,
+			      struct ibv_qp_init_attr_ex *attr_ex,
+			      struct ibv_command_buffer *link)
+{
+	DECLARE_FBCMD_BUFFER(cmdb, UVERBS_OBJECT_QP, UVERBS_METHOD_QP_CREATE, 15, link);
+	struct ib_uverbs_attr *handle;
+	uint32_t qp_num;
+	uint32_t pd_handle;
+	uint32_t send_cq_handle = 0;
+	uint32_t recv_cq_handle = 0;
+	int ret;
+	struct ibv_qp *qp = vqp ? &vqp->qp : qp_in;
+	struct verbs_xrcd *vxrcd = NULL;
+	uint32_t create_flags = 0;
+
+	qp->context = context;
+
+	switch (attr_ex->qp_type) {
+	case IBV_QPT_XRC_RECV:
+		if (!(attr_ex->comp_mask & IBV_QP_INIT_ATTR_XRCD)) {
+			errno = EINVAL;
+			return errno;
+		}
+
+		vxrcd = container_of(attr_ex->xrcd, struct verbs_xrcd, xrcd);
+		fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_XRCD_HANDLE, vxrcd->handle);
+		pd_handle = vxrcd->handle;
+		break;
+	case IBV_QPT_RC:
+	case IBV_QPT_UD:
+	case IBV_QPT_UC:
+	case IBV_QPT_RAW_PACKET:
+	case IBV_QPT_XRC_SEND:
+	case IBV_QPT_DRIVER:
+		if (!(attr_ex->comp_mask & IBV_QP_INIT_ATTR_PD)) {
+			errno = EINVAL;
+			return errno;
+		}
+
+		fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_PD_HANDLE, attr_ex->pd->handle);
+		pd_handle = attr_ex->pd->handle;
+
+		if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_IND_TABLE) {
+			if (attr_ex->cap.max_recv_wr || attr_ex->cap.max_recv_sge ||
+				attr_ex->recv_cq || attr_ex->srq) {
+				errno = EINVAL;
+				return errno;
+			}
+
+			fallback_require_ex(cmdb);
+			fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_IND_TABLE_HANDLE,
+				 attr_ex->rwq_ind_tbl->ind_tbl_handle);
+
+			/* send_cq is optinal */
+			if (attr_ex->cap.max_send_wr) {
+				fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE,
+						 attr_ex->send_cq->handle);
+				send_cq_handle = attr_ex->send_cq->handle;
+			}
+		} else {
+			fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_SEND_CQ_HANDLE,
+				 attr_ex->send_cq->handle);
+			send_cq_handle = attr_ex->send_cq->handle;
+
+			if (attr_ex->qp_type != IBV_QPT_XRC_SEND) {
+				fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_RECV_CQ_HANDLE,
+						 attr_ex->recv_cq->handle);
+				recv_cq_handle = attr_ex->recv_cq->handle;
+			}
+		}
+
+		/* compatible with kernel code from the 'write' mode */
+		if (attr_ex->qp_type == IBV_QPT_XRC_SEND) {
+			attr_ex->cap.max_recv_wr = 0;
+			attr_ex->cap.max_recv_sge = 0;
+		}
+
+		break;
+	default:
+		errno = EINVAL;
+		return errno;
+	}
+
+	handle = fill_attr_out_obj(cmdb, UVERBS_ATTR_CREATE_QP_HANDLE);
+	fill_attr_const_in(cmdb, UVERBS_ATTR_CREATE_QP_TYPE, attr_ex->qp_type);
+	fill_attr_in_uint64(cmdb, UVERBS_ATTR_CREATE_QP_USER_HANDLE, (uintptr_t)qp);
+
+	static_assert(offsetof(struct ibv_qp_cap, max_send_wr) ==
+		offsetof(struct ib_uverbs_qp_cap, max_send_wr), "Bad layout");
+	static_assert(offsetof(struct ibv_qp_cap, max_recv_wr) ==
+		offsetof(struct ib_uverbs_qp_cap, max_recv_wr), "Bad layout");
+	static_assert(offsetof(struct ibv_qp_cap, max_send_sge) ==
+		offsetof(struct ib_uverbs_qp_cap, max_send_sge), "Bad layout");
+	static_assert(offsetof(struct ibv_qp_cap, max_recv_sge) ==
+		offsetof(struct ib_uverbs_qp_cap, max_recv_sge), "Bad layout");
+	static_assert(offsetof(struct ibv_qp_cap, max_inline_data) ==
+		offsetof(struct ib_uverbs_qp_cap, max_inline_data), "Bad layout");
+
+	fill_attr_in_ptr(cmdb, UVERBS_ATTR_CREATE_QP_CAP, &attr_ex->cap);
+	fill_attr_in_fd(cmdb, UVERBS_ATTR_CREATE_QP_EVENT_FD, context->async_fd);
+
+	if (attr_ex->sq_sig_all)
+		create_flags |= IB_UVERBS_QP_CREATE_SQ_SIG_ALL;
+
+	if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_CREATE_FLAGS) {
+		if (attr_ex->create_flags & ~CREATE_QP_EX_SUP_CREATE_FLAGS) {
+			errno = EINVAL;
+			return errno;
+		}
+
+		fallback_require_ex(cmdb);
+		create_flags |= attr_ex->create_flags;
+
+		if (attr_ex->create_flags & IBV_QP_CREATE_SOURCE_QPN) {
+			fill_attr_in_uint32(cmdb, UVERBS_ATTR_CREATE_QP_SOURCE_QPN,
+					    attr_ex->source_qpn);
+			/* source QPN is a self attribute once moving to ioctl,
+			 * no extra bit is supported.
+			 */
+			create_flags &= ~IBV_QP_CREATE_SOURCE_QPN;
+		}
+	}
+
+	if (create_flags)
+		fill_attr_in_uint32(cmdb, UVERBS_ATTR_CREATE_QP_FLAGS,
+				    create_flags);
+
+	if (attr_ex->srq)
+		fill_attr_in_obj(cmdb, UVERBS_ATTR_CREATE_QP_SRQ_HANDLE, attr_ex->srq->handle);
+
+	fill_attr_out_ptr(cmdb, UVERBS_ATTR_CREATE_QP_RESP_CAP, &attr_ex->cap);
+	fill_attr_out_ptr(cmdb, UVERBS_ATTR_CREATE_QP_RESP_QP_NUM, &qp_num);
+
+	switch (execute_ioctl_fallback(context, create_qp, cmdb, &ret)) {
+	case TRY_WRITE: {
+		if (abi_ver > 4) {
+			DECLARE_LEGACY_UHW_BUFS(link, IB_USER_VERBS_CMD_CREATE_QP);
+
+			*req = (struct ib_uverbs_create_qp){
+				.pd_handle = pd_handle,
+				.user_handle = (uintptr_t)qp,
+				.max_send_wr = attr_ex->cap.max_send_wr,
+				.max_recv_wr = attr_ex->cap.max_recv_wr,
+				.max_send_sge = attr_ex->cap.max_send_sge,
+				.max_recv_sge = attr_ex->cap.max_recv_sge,
+				.max_inline_data = attr_ex->cap.max_inline_data,
+				.sq_sig_all = attr_ex->sq_sig_all,
+				.qp_type = attr_ex->qp_type,
+				.srq_handle = attr_ex->srq ? attr_ex->srq->handle : 0,
+				.is_srq = !!attr_ex->srq,
+				.recv_cq_handle = recv_cq_handle,
+				.send_cq_handle = send_cq_handle,
+			};
+
+			ret = execute_write_bufs(
+				context, IB_USER_VERBS_CMD_CREATE_QP, req, resp);
+			if (ret)
+				return ret;
+
+			qp->handle = resp->qp_handle;
+			qp->qp_num = resp->qpn;
+
+			attr_ex->cap.max_recv_sge    = resp->max_recv_sge;
+			attr_ex->cap.max_send_sge    = resp->max_send_sge;
+			attr_ex->cap.max_recv_wr     = resp->max_recv_wr;
+			attr_ex->cap.max_send_wr     = resp->max_send_wr;
+			attr_ex->cap.max_inline_data = resp->max_inline_data;
+
+		} else if (abi_ver == 4) {
+			DECLARE_LEGACY_UHW_BUFS(link, IB_USER_VERBS_CMD_CREATE_QP_V4);
+
+			*req = (struct ib_uverbs_create_qp){
+				.pd_handle = pd_handle,
+				.user_handle = (uintptr_t)qp,
+				.max_send_wr = attr_ex->cap.max_send_wr,
+				.max_recv_wr = attr_ex->cap.max_recv_wr,
+				.max_send_sge = attr_ex->cap.max_send_sge,
+				.max_recv_sge = attr_ex->cap.max_recv_sge,
+				.max_inline_data = attr_ex->cap.max_inline_data,
+				.sq_sig_all = attr_ex->sq_sig_all,
+				.qp_type = attr_ex->qp_type,
+				.srq_handle = attr_ex->srq ? attr_ex->srq->handle : 0,
+				.is_srq = !!attr_ex->srq,
+				.recv_cq_handle = recv_cq_handle,
+				.send_cq_handle = send_cq_handle,
+			};
+
+			ret = execute_write_bufs(
+				context, IB_USER_VERBS_CMD_CREATE_QP_V4, req, resp);
+			if (ret)
+				return ret;
+
+			qp->handle = resp->qp_handle;
+			qp->qp_num = resp->qpn;
+
+			attr_ex->cap.max_recv_sge    = resp->max_recv_sge;
+			attr_ex->cap.max_send_sge    = resp->max_send_sge;
+			attr_ex->cap.max_recv_wr     = resp->max_recv_wr;
+			attr_ex->cap.max_send_wr     = resp->max_send_wr;
+			attr_ex->cap.max_inline_data = resp->max_inline_data;
+		} else {
+			DECLARE_LEGACY_UHW_BUFS(link, IB_USER_VERBS_CMD_CREATE_QP_V3);
+
+			*req = (struct ib_uverbs_create_qp){
+				.pd_handle = pd_handle,
+				.user_handle = (uintptr_t)qp,
+				.max_send_wr = attr_ex->cap.max_send_wr,
+				.max_recv_wr = attr_ex->cap.max_recv_wr,
+				.max_send_sge = attr_ex->cap.max_send_sge,
+				.max_recv_sge = attr_ex->cap.max_recv_sge,
+				.max_inline_data = attr_ex->cap.max_inline_data,
+				.sq_sig_all = attr_ex->sq_sig_all,
+				.qp_type = attr_ex->qp_type,
+				.srq_handle = attr_ex->srq ? attr_ex->srq->handle : 0,
+				.is_srq = !!attr_ex->srq,
+				.recv_cq_handle = recv_cq_handle,
+				.send_cq_handle = send_cq_handle,
+			};
+
+			ret = execute_write_bufs(
+				context, IB_USER_VERBS_CMD_CREATE_QP_V3, req, resp);
+			if (ret)
+				return ret;
+
+			qp->handle = resp->qp_handle;
+			qp->qp_num = resp->qpn;
+		}
+
+		set_qp(vqp, qp, attr_ex, vxrcd);
+		return 0;
+	}
+
+	case TRY_WRITE_EX: {
+		DECLARE_LEGACY_UHW_BUFS_EX(link,
+					   IB_USER_VERBS_EX_CMD_CREATE_QP);
+
+		*req = (struct ib_uverbs_ex_create_qp){
+			.pd_handle = pd_handle,
+			.user_handle = (uintptr_t)qp,
+			.max_send_wr = attr_ex->cap.max_send_wr,
+			.max_recv_wr = attr_ex->cap.max_recv_wr,
+			.max_send_sge = attr_ex->cap.max_send_sge,
+			.max_recv_sge = attr_ex->cap.max_recv_sge,
+			.max_inline_data = attr_ex->cap.max_inline_data,
+			.sq_sig_all = attr_ex->sq_sig_all,
+			.qp_type = attr_ex->qp_type,
+			.srq_handle = attr_ex->srq ? attr_ex->srq->handle : 0,
+			.is_srq = !!attr_ex->srq,
+			.recv_cq_handle = recv_cq_handle,
+			.send_cq_handle = send_cq_handle,
+		};
+
+		if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_CREATE_FLAGS) {
+			req->create_flags = attr_ex->create_flags;
+
+			if (attr_ex->create_flags & IBV_QP_CREATE_SOURCE_QPN)
+				req->source_qpn = attr_ex->source_qpn;
+		}
+
+		if (attr_ex->comp_mask & IBV_QP_INIT_ATTR_IND_TABLE) {
+			req->rwq_ind_tbl_handle = attr_ex->rwq_ind_tbl->ind_tbl_handle;
+			req->comp_mask = IB_UVERBS_CREATE_QP_MASK_IND_TABLE;
+		}
+
+		ret = execute_write_bufs_ex(
+			context, IB_USER_VERBS_EX_CMD_CREATE_QP, req, resp);
+		if (ret)
+			return ret;
+
+		qp->handle = resp->base.qp_handle;
+		qp->qp_num = resp->base.qpn;
+
+		attr_ex->cap.max_recv_sge    = resp->base.max_recv_sge;
+		attr_ex->cap.max_send_sge    = resp->base.max_send_sge;
+		attr_ex->cap.max_recv_wr     = resp->base.max_recv_wr;
+		attr_ex->cap.max_send_wr     = resp->base.max_send_wr;
+		attr_ex->cap.max_inline_data = resp->base.max_inline_data;
+		set_qp(vqp, qp, attr_ex, vxrcd);
+		return 0;
+	}
+
+	case SUCCESS:
+		break;
+
+	default:
+		return ret;
+	}
+
+	qp->handle = read_attr_obj(UVERBS_ATTR_CREATE_QP_HANDLE, handle);
+	qp->qp_num = qp_num;
+	set_qp(vqp, qp, attr_ex, vxrcd);
+
+	return 0;
+}
+
+int ibv_cmd_create_qp(struct ibv_pd *pd,
+		      struct ibv_qp *qp, struct ibv_qp_init_attr *attr,
+		      struct ibv_create_qp *cmd, size_t cmd_size,
+		      struct ib_uverbs_create_qp_resp *resp, size_t resp_size)
+{
+	DECLARE_CMD_BUFFER_COMPAT(cmdb, UVERBS_OBJECT_QP,
+				  UVERBS_METHOD_QP_CREATE, cmd, cmd_size, resp,
+				  resp_size);
+
+	struct ibv_qp_init_attr_ex attr_ex = {};
+	int ret;
+
+	memcpy(&attr_ex, attr, sizeof(*attr));
+	attr_ex.comp_mask |= IBV_QP_INIT_ATTR_PD;
+	attr_ex.pd = pd;
+	ret = ibv_icmd_create_qp(pd->context, NULL, qp, &attr_ex, cmdb);
+	if (!ret)
+		memcpy(&attr->cap, &attr_ex.cap, sizeof(attr_ex.cap));
+
+	return ret;
+}
+
+int ibv_cmd_create_qp_ex(struct ibv_context *context,
+			 struct verbs_qp *qp,
+			 struct ibv_qp_init_attr_ex *attr_ex,
+			 struct ibv_create_qp *cmd, size_t cmd_size,
+			 struct ib_uverbs_create_qp_resp *resp, size_t resp_size)
+{
+	DECLARE_CMD_BUFFER_COMPAT(cmdb, UVERBS_OBJECT_QP,
+				  UVERBS_METHOD_QP_CREATE, cmd, cmd_size, resp,
+				  resp_size);
+
+	if (!check_comp_mask(attr_ex->comp_mask,
+			     IBV_QP_INIT_ATTR_PD |
+			     IBV_QP_INIT_ATTR_XRCD |
+			     IBV_QP_INIT_ATTR_SEND_OPS_FLAGS)) {
+		errno = EINVAL;
+		return errno;
+	}
+
+	return ibv_icmd_create_qp(context, qp, NULL, attr_ex, cmdb);
+}
+
+int ibv_cmd_create_qp_ex2(struct ibv_context *context,
+			  struct verbs_qp *qp,
+			  struct ibv_qp_init_attr_ex *attr_ex,
+			  struct ibv_create_qp_ex *cmd,
+			  size_t cmd_size,
+			  struct ib_uverbs_ex_create_qp_resp *resp,
+			  size_t resp_size)
+{
+	DECLARE_CMD_BUFFER_COMPAT(cmdb, UVERBS_OBJECT_QP,
+				  UVERBS_METHOD_QP_CREATE, cmd, cmd_size, resp,
+				  resp_size);
+
+	if (!check_comp_mask(attr_ex->comp_mask,
+			     IBV_QP_INIT_ATTR_PD |
+			     IBV_QP_INIT_ATTR_XRCD |
+			     IBV_QP_INIT_ATTR_CREATE_FLAGS |
+			     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)) {
+		errno = EINVAL;
+		return errno;
+	}
+
+	return ibv_icmd_create_qp(context, qp, NULL, attr_ex, cmdb);
+}
+
+int ibv_cmd_destroy_qp(struct ibv_qp *qp)
+{
+	DECLARE_FBCMD_BUFFER(cmdb, UVERBS_OBJECT_QP, UVERBS_METHOD_QP_DESTROY, 2,
+			     NULL);
+	struct ib_uverbs_destroy_qp_resp resp;
+	int ret;
+
+	fill_attr_out_ptr(cmdb, UVERBS_ATTR_DESTROY_QP_RESP, &resp);
+	fill_attr_in_obj(cmdb, UVERBS_ATTR_DESTROY_QP_HANDLE, qp->handle);
+
+	switch (execute_ioctl_fallback(qp->context, destroy_qp, cmdb, &ret)) {
+	case TRY_WRITE: {
+		struct ibv_destroy_qp req;
+
+		req.core_payload = (struct ib_uverbs_destroy_qp){
+			.qp_handle = qp->handle,
+		};
+
+		ret = execute_cmd_write(qp->context,
+					IB_USER_VERBS_CMD_DESTROY_QP, &req,
+					sizeof(req), &resp, sizeof(resp));
+		break;
+	}
+
+	default:
+		break;
+	}
+
+	if (verbs_is_destroy_err(&ret))
+		return ret;
+
+	pthread_mutex_lock(&qp->mutex);
+	while (qp->events_completed != resp.events_reported)
+		pthread_cond_wait(&qp->cond, &qp->mutex);
+	pthread_mutex_unlock(&qp->mutex);
+
+	return 0;
+}
diff --git a/libibverbs/driver.h b/libibverbs/driver.h
index 9a6a8ae..de81955 100644
--- a/libibverbs/driver.h
+++ b/libibverbs/driver.h
@@ -521,12 +521,12 @@ int ibv_cmd_create_qp(struct ibv_pd *pd,
 		      struct ibv_create_qp *cmd, size_t cmd_size,
 		      struct ib_uverbs_create_qp_resp *resp, size_t resp_size);
 int ibv_cmd_create_qp_ex(struct ibv_context *context,
-			 struct verbs_qp *qp, int vqp_sz,
+			 struct verbs_qp *qp,
 			 struct ibv_qp_init_attr_ex *attr_ex,
 			 struct ibv_create_qp *cmd, size_t cmd_size,
 			 struct ib_uverbs_create_qp_resp *resp, size_t resp_size);
 int ibv_cmd_create_qp_ex2(struct ibv_context *context,
-			  struct verbs_qp *qp, int vqp_sz,
+			  struct verbs_qp *qp,
 			  struct ibv_qp_init_attr_ex *qp_attr,
 			  struct ibv_create_qp_ex *cmd,
 			  size_t cmd_size,
diff --git a/libibverbs/kern-abi.h b/libibverbs/kern-abi.h
index ef9c3f6..1238569 100644
--- a/libibverbs/kern-abi.h
+++ b/libibverbs/kern-abi.h
@@ -312,4 +312,11 @@ struct ibv_create_srq_resp_v5 {
 enum { IB_USER_VERBS_CMD_CREATE_SRQ_V5 = IB_USER_VERBS_CMD_CREATE_SRQ };
 DECLARE_CMDX(IB_USER_VERBS_CMD_CREATE_SRQ_V5, ibv_create_srq_v5, ib_uverbs_create_srq, ibv_create_srq_resp_v5);
 
+#define _STRUCT_ib_uverbs_create_qp_v4
+enum { IB_USER_VERBS_CMD_CREATE_QP_V4 = IB_USER_VERBS_CMD_CREATE_QP };
+DECLARE_CMDX(IB_USER_VERBS_CMD_CREATE_QP_V4, ibv_create_qp_v4, ib_uverbs_create_qp, ibv_create_qp_resp_v4);
+
+#define _STRUCT_ib_uverbs_create_qp_v3
+enum { IB_USER_VERBS_CMD_CREATE_QP_V3 = IB_USER_VERBS_CMD_CREATE_QP };
+DECLARE_CMDX(IB_USER_VERBS_CMD_CREATE_QP_V3, ibv_create_qp_v3, ib_uverbs_create_qp, ibv_create_qp_resp_v3);
 #endif /* KERN_ABI_H */
diff --git a/libibverbs/verbs.c b/libibverbs/verbs.c
index 629f24c..f380036 100644
--- a/libibverbs/verbs.c
+++ b/libibverbs/verbs.c
@@ -600,20 +600,6 @@ LATEST_SYMVER_FUNC(ibv_create_qp, 1_1, "IBVERBS_1.1",
 {
 	struct ibv_qp *qp = get_ops(pd->context)->create_qp(pd, qp_init_attr);
 
-	if (qp) {
-		qp->context    	     = pd->context;
-		qp->qp_context 	     = qp_init_attr->qp_context;
-		qp->pd         	     = pd;
-		qp->send_cq    	     = qp_init_attr->send_cq;
-		qp->recv_cq    	     = qp_init_attr->recv_cq;
-		qp->srq        	     = qp_init_attr->srq;
-		qp->qp_type          = qp_init_attr->qp_type;
-		qp->state	     = IBV_QPS_RESET;
-		qp->events_completed = 0;
-		pthread_mutex_init(&qp->mutex, NULL);
-		pthread_cond_init(&qp->cond, NULL);
-	}
-
 	return qp;
 }
 
diff --git a/providers/efa/verbs.c b/providers/efa/verbs.c
index 03b8cf9..5f8e7b8 100644
--- a/providers/efa/verbs.c
+++ b/providers/efa/verbs.c
@@ -826,7 +826,7 @@ static struct ibv_qp *create_qp(struct ibv_context *ibvctx,
 	if (attr->qp_type == IBV_QPT_DRIVER)
 		req.driver_qp_type = efa_attr->driver_qp_type;
 
-	err = ibv_cmd_create_qp_ex(ibvctx, &qp->verbs_qp, sizeof(qp->verbs_qp),
+	err = ibv_cmd_create_qp_ex(ibvctx, &qp->verbs_qp,
 				   attr, &req.ibv_cmd, sizeof(req),
 				   &resp.ibv_resp, sizeof(resp));
 	if (err)
diff --git a/providers/mlx4/verbs.c b/providers/mlx4/verbs.c
index 010cd6c..c68cc35 100644
--- a/providers/mlx4/verbs.c
+++ b/providers/mlx4/verbs.c
@@ -790,7 +790,7 @@ static int mlx4_cmd_create_qp_ex_rss(struct ibv_context *context,
 	       sizeof(cmd_ex.rx_hash_key));
 
 	ret = ibv_cmd_create_qp_ex2(context, &qp->verbs_qp,
-				    sizeof(qp->verbs_qp), attr, &cmd_ex.ibv_cmd,
+				    attr, &cmd_ex.ibv_cmd,
 				    sizeof(cmd_ex), &resp.ibv_resp,
 				    sizeof(resp));
 	return ret;
@@ -849,7 +849,7 @@ static int mlx4_cmd_create_qp_ex(struct ibv_context *context,
 	cmd_ex.drv_payload = cmd->drv_payload;
 
 	ret = ibv_cmd_create_qp_ex2(context, &qp->verbs_qp,
-				    sizeof(qp->verbs_qp), attr, &cmd_ex.ibv_cmd,
+				    attr, &cmd_ex.ibv_cmd,
 				    sizeof(cmd_ex), &resp.ibv_resp,
 				    sizeof(resp));
 	return ret;
@@ -981,7 +981,7 @@ static struct ibv_qp *create_qp_ex(struct ibv_context *context,
 		ret = mlx4_cmd_create_qp_ex(context, attr, &cmd, qp);
 	else
 		ret = ibv_cmd_create_qp_ex(context, &qp->verbs_qp,
-					   sizeof(qp->verbs_qp), attr,
+					   attr,
 					   &cmd.ibv_cmd, sizeof(cmd), &resp,
 					   sizeof(resp));
 	if (ret)
diff --git a/providers/mlx5/verbs.c b/providers/mlx5/verbs.c
index 830c5b1..ddf84f7 100644
--- a/providers/mlx5/verbs.c
+++ b/providers/mlx5/verbs.c
@@ -1866,7 +1866,7 @@ static int mlx5_cmd_create_rss_qp(struct ibv_context *context,
 			attr->rx_hash_conf.rx_hash_key_len);
 
 	ret = ibv_cmd_create_qp_ex2(context, &qp->verbs_qp,
-				    sizeof(qp->verbs_qp), attr,
+				    attr,
 				    &cmd_ex_rss.ibv_cmd, sizeof(cmd_ex_rss),
 				    &resp.ibv_resp, sizeof(resp));
 	if (ret)
@@ -1899,7 +1899,7 @@ static int mlx5_cmd_create_qp_ex(struct ibv_context *context,
 	cmd_ex.drv_payload = cmd->drv_payload;
 
 	ret = ibv_cmd_create_qp_ex2(context, &qp->verbs_qp,
-				    sizeof(qp->verbs_qp), attr, &cmd_ex.ibv_cmd,
+				    attr, &cmd_ex.ibv_cmd,
 				    sizeof(cmd_ex), &resp->ibv_resp,
 				    sizeof(*resp));
 
@@ -1990,7 +1990,7 @@ static int create_dct(struct ibv_context *context,
 	}
 	cmd.uidx = usr_idx;
 
-	ret = ibv_cmd_create_qp_ex(context, &qp->verbs_qp, sizeof(qp->verbs_qp),
+	ret = ibv_cmd_create_qp_ex(context, &qp->verbs_qp,
 				   attr, &cmd.ibv_cmd, sizeof(cmd),
 				   &resp.ibv_resp, sizeof(resp));
 	if (ret) {
@@ -2290,7 +2290,7 @@ static struct ibv_qp *create_qp(struct ibv_context *context,
 	if (attr->comp_mask & MLX5_CREATE_QP_EX2_COMP_MASK)
 		ret = mlx5_cmd_create_qp_ex(context, attr, &cmd, qp, &resp_ex);
 	else
-		ret = ibv_cmd_create_qp_ex(context, &qp->verbs_qp, sizeof(qp->verbs_qp),
+		ret = ibv_cmd_create_qp_ex(context, &qp->verbs_qp,
 					   attr, &cmd.ibv_cmd, sizeof(cmd),
 					   &resp.ibv_resp, sizeof(resp));
 	if (ret) {
