put_uobj_read(wq->uobject);
 }
 
+static struct ib_rwq_ind_table *idr_read_rwq_indirection_table(int ind_table_handle,
+                                                              struct ib_ucontext *context)
+{
+       return idr_read_obj(&ib_uverbs_rwq_ind_tbl_idr, ind_table_handle, context, 0);
+}
+
+static void put_rwq_indirection_table_read(struct ib_rwq_ind_table *ind_table)
+{
+       put_uobj_read(ind_table->uobject);
+}
+
 static struct ib_qp *idr_write_qp(int qp_handle, struct ib_ucontext *context)
 {
        struct ib_uobject *uobj;
        struct ib_srq                   *srq = NULL;
        struct ib_qp                    *qp;
        char                            *buf;
-       struct ib_qp_init_attr          attr;
+       struct ib_qp_init_attr          attr = {};
        struct ib_uverbs_ex_create_qp_resp resp;
        int                             ret;
+       struct ib_rwq_ind_table *ind_tbl = NULL;
+       bool has_sq = true;
 
        if (cmd->qp_type == IB_QPT_RAW_PACKET && !capable(CAP_NET_RAW))
                return -EPERM;
        init_uobj(&obj->uevent.uobject, cmd->user_handle, file->ucontext,
                  &qp_lock_class);
        down_write(&obj->uevent.uobject.mutex);
+       if (cmd_sz >= offsetof(typeof(*cmd), rwq_ind_tbl_handle) +
+                     sizeof(cmd->rwq_ind_tbl_handle) &&
+                     (cmd->comp_mask & IB_UVERBS_CREATE_QP_MASK_IND_TABLE)) {
+               ind_tbl = idr_read_rwq_indirection_table(cmd->rwq_ind_tbl_handle,
+                                                        file->ucontext);
+               if (!ind_tbl) {
+                       ret = -EINVAL;
+                       goto err_put;
+               }
+
+               attr.rwq_ind_tbl = ind_tbl;
+       }
+
+       if ((cmd_sz >= offsetof(typeof(*cmd), reserved1) +
+                      sizeof(cmd->reserved1)) && cmd->reserved1) {
+               ret = -EOPNOTSUPP;
+               goto err_put;
+       }
+
+       if (ind_tbl && (cmd->max_recv_wr || cmd->max_recv_sge || cmd->is_srq)) {
+               ret = -EINVAL;
+               goto err_put;
+       }
+
+       if (ind_tbl && !cmd->max_send_wr)
+               has_sq = false;
 
        if (cmd->qp_type == IB_QPT_XRC_TGT) {
                xrcd = idr_read_xrcd(cmd->pd_handle, file->ucontext,
                                }
                        }
 
-                       if (cmd->recv_cq_handle != cmd->send_cq_handle) {
-                               rcq = idr_read_cq(cmd->recv_cq_handle,
-                                                 file->ucontext, 0);
-                               if (!rcq) {
-                                       ret = -EINVAL;
-                                       goto err_put;
+                       if (!ind_tbl) {
+                               if (cmd->recv_cq_handle != cmd->send_cq_handle) {
+                                       rcq = idr_read_cq(cmd->recv_cq_handle,
+                                                         file->ucontext, 0);
+                                       if (!rcq) {
+                                               ret = -EINVAL;
+                                               goto err_put;
+                                       }
                                }
                        }
                }
 
-               scq = idr_read_cq(cmd->send_cq_handle, file->ucontext, !!rcq);
-               rcq = rcq ?: scq;
+               if (has_sq)
+                       scq = idr_read_cq(cmd->send_cq_handle, file->ucontext, !!rcq);
+               if (!ind_tbl)
+                       rcq = rcq ?: scq;
                pd  = idr_read_pd(cmd->pd_handle, file->ucontext);
-               if (!pd || !scq) {
+               if (!pd || (!scq && has_sq)) {
                        ret = -EINVAL;
                        goto err_put;
                }
                qp->send_cq       = attr.send_cq;
                qp->recv_cq       = attr.recv_cq;
                qp->srq           = attr.srq;
+               qp->rwq_ind_tbl   = ind_tbl;
                qp->event_handler = attr.event_handler;
                qp->qp_context    = attr.qp_context;
                qp->qp_type       = attr.qp_type;
                atomic_set(&qp->usecnt, 0);
                atomic_inc(&pd->usecnt);
-               atomic_inc(&attr.send_cq->usecnt);
+               if (attr.send_cq)
+                       atomic_inc(&attr.send_cq->usecnt);
                if (attr.recv_cq)
                        atomic_inc(&attr.recv_cq->usecnt);
                if (attr.srq)
                        atomic_inc(&attr.srq->usecnt);
+               if (ind_tbl)
+                       atomic_inc(&ind_tbl->usecnt);
        }
        qp->uobject = &obj->uevent.uobject;
 
                put_cq_read(rcq);
        if (srq)
                put_srq_read(srq);
+       if (ind_tbl)
+               put_rwq_indirection_table_read(ind_tbl);
 
        mutex_lock(&file->mutex);
        list_add_tail(&obj->uevent.uobject.list, &file->ucontext->qp_list);
                put_cq_read(rcq);
        if (srq)
                put_srq_read(srq);
+       if (ind_tbl)
+               put_rwq_indirection_table_read(ind_tbl);
 
        put_uobj_write(&obj->uevent.uobject);
        return ret;
        if (err)
                return err;
 
-       if (cmd.comp_mask)
+       if (cmd.comp_mask & ~IB_UVERBS_CREATE_QP_SUP_COMP_MASK)
                return -EINVAL;
 
        if (cmd.reserved)