IB: refcount race fixes
[powerpc.git] / drivers / infiniband / core / uverbs_cmd.c
index ed45da8..9f69bd4 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * Copyright (c) 2005 Topspin Communications.  All rights reserved.
- * Copyright (c) 2005 Cisco Systems.  All rights reserved.
+ * Copyright (c) 2005, 2006 Cisco Systems.  All rights reserved.
  * Copyright (c) 2005 PathScale, Inc.  All rights reserved.
+ * Copyright (c) 2006 Mellanox Technologies.  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
@@ -67,7 +68,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
 
        if (file->ucontext) {
                ret = -EINVAL;
@@ -119,7 +120,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
 
        fd_install(resp.async_fd, filp);
 
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
        return in_len;
 
@@ -131,7 +132,7 @@ err_free:
        ibdev->dealloc_ucontext(ucontext);
 
 err:
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
        return ret;
 }
 
@@ -157,7 +158,7 @@ ssize_t ib_uverbs_query_device(struct ib_uverbs_file *file,
        memset(&resp, 0, sizeof resp);
 
        resp.fw_ver                    = attr.fw_ver;
-       resp.node_guid                 = attr.node_guid;
+       resp.node_guid                 = file->device->ib_dev->node_guid;
        resp.sys_image_guid            = attr.sys_image_guid;
        resp.max_mr_size               = attr.max_mr_size;
        resp.page_size_cap             = attr.page_size_cap;
@@ -290,7 +291,7 @@ ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file,
        pd->uobject = uobj;
        atomic_set(&pd->usecnt, 0);
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
 retry:
        if (!idr_pre_get(&ib_uverbs_pd_idr, GFP_KERNEL)) {
@@ -314,11 +315,11 @@ retry:
                goto err_idr;
        }
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_add_tail(&uobj->list, &file->ucontext->pd_list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return in_len;
 
@@ -326,7 +327,7 @@ err_idr:
        idr_remove(&ib_uverbs_pd_idr, uobj->id);
 
 err_up:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
        ib_dealloc_pd(pd);
 
 err:
@@ -346,7 +347,7 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        pd = idr_find(&ib_uverbs_pd_idr, cmd.pd_handle);
        if (!pd || pd->uobject->context != file->ucontext)
@@ -360,14 +361,14 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,
 
        idr_remove(&ib_uverbs_pd_idr, cmd.pd_handle);
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_del(&uobj->list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
        kfree(uobj);
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret ? ret : in_len;
 }
@@ -426,7 +427,7 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,
 
        obj->umem.virt_base = cmd.hca_va;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        pd = idr_find(&ib_uverbs_pd_idr, cmd.pd_handle);
        if (!pd || pd->uobject->context != file->ucontext) {
@@ -476,11 +477,11 @@ retry:
                goto err_idr;
        }
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_add_tail(&obj->uobject.list, &file->ucontext->mr_list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return in_len;
 
@@ -489,9 +490,10 @@ err_idr:
 
 err_unreg:
        ib_dereg_mr(mr);
+       atomic_dec(&pd->usecnt);
 
 err_up:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        ib_umem_release(file->device->ib_dev, &obj->umem);
 
@@ -512,7 +514,7 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file,
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        mr = idr_find(&ib_uverbs_mr_idr, cmd.mr_handle);
        if (!mr || mr->uobject->context != file->ucontext)
@@ -526,15 +528,15 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file,
 
        idr_remove(&ib_uverbs_mr_idr, cmd.mr_handle);
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_del(&memobj->uobject.list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
        ib_umem_release(file->device->ib_dev, &memobj->umem);
        kfree(memobj);
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret ? ret : in_len;
 }
@@ -593,13 +595,18 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
        if (cmd.comp_vector >= file->device->num_comp_vectors)
                return -EINVAL;
 
-       if (cmd.comp_channel >= 0)
-               ev_file = ib_uverbs_lookup_comp_file(cmd.comp_channel);
-
        uobj = kmalloc(sizeof *uobj, GFP_KERNEL);
        if (!uobj)
                return -ENOMEM;
 
+       if (cmd.comp_channel >= 0) {
+               ev_file = ib_uverbs_lookup_comp_file(cmd.comp_channel);
+               if (!ev_file) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+       }
+
        uobj->uobject.user_handle   = cmd.user_handle;
        uobj->uobject.context       = file->ucontext;
        uobj->uverbs_file           = file;
@@ -622,7 +629,7 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
        cq->cq_context    = ev_file;
        atomic_set(&cq->usecnt, 0);
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
 retry:
        if (!idr_pre_get(&ib_uverbs_cq_idr, GFP_KERNEL)) {
@@ -647,11 +654,11 @@ retry:
                goto err_idr;
        }
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_add_tail(&uobj->uobject.list, &file->ucontext->cq_list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return in_len;
 
@@ -659,14 +666,56 @@ err_idr:
        idr_remove(&ib_uverbs_cq_idr, uobj->uobject.id);
 
 err_up:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
        ib_destroy_cq(cq);
 
 err:
+       if (ev_file)
+               ib_uverbs_release_ucq(file, ev_file, uobj);
        kfree(uobj);
        return ret;
 }
 
+ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file,
+                           const char __user *buf, int in_len,
+                           int out_len)
+{
+       struct ib_uverbs_resize_cq      cmd;
+       struct ib_uverbs_resize_cq_resp resp;
+       struct ib_udata                 udata;
+       struct ib_cq                    *cq;
+       int                             ret = -EINVAL;
+
+       if (copy_from_user(&cmd, buf, sizeof cmd))
+               return -EFAULT;
+
+       INIT_UDATA(&udata, buf + sizeof cmd,
+                  (unsigned long) cmd.response + sizeof resp,
+                  in_len - sizeof cmd, out_len - sizeof resp);
+
+       mutex_lock(&ib_uverbs_idr_mutex);
+
+       cq = idr_find(&ib_uverbs_cq_idr, cmd.cq_handle);
+       if (!cq || cq->uobject->context != file->ucontext || !cq->device->resize_cq)
+               goto out;
+
+       ret = cq->device->resize_cq(cq, cmd.cqe, &udata);
+       if (ret)
+               goto out;
+
+       memset(&resp, 0, sizeof resp);
+       resp.cqe = cq->cqe;
+
+       if (copy_to_user((void __user *) (unsigned long) cmd.response,
+                        &resp, sizeof resp))
+               ret = -EFAULT;
+
+out:
+       mutex_unlock(&ib_uverbs_idr_mutex);
+
+       return ret ? ret : in_len;
+}
+
 ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
                          const char __user *buf, int in_len,
                          int out_len)
@@ -693,7 +742,7 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
                goto out_wc;
        }
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
        cq = idr_find(&ib_uverbs_cq_idr, cmd.cq_handle);
        if (!cq || cq->uobject->context != file->ucontext) {
                ret = -EINVAL;
@@ -723,7 +772,7 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
                ret = -EFAULT;
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
        kfree(resp);
 
 out_wc:
@@ -742,14 +791,14 @@ ssize_t ib_uverbs_req_notify_cq(struct ib_uverbs_file *file,
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
        cq = idr_find(&ib_uverbs_cq_idr, cmd.cq_handle);
        if (cq && cq->uobject->context == file->ucontext) {
                ib_req_notify_cq(cq, cmd.solicited_only ?
                                        IB_CQ_SOLICITED : IB_CQ_NEXT_COMP);
                ret = in_len;
        }
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret;
 }
@@ -771,7 +820,7 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file,
 
        memset(&resp, 0, sizeof resp);
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        cq = idr_find(&ib_uverbs_cq_idr, cmd.cq_handle);
        if (!cq || cq->uobject->context != file->ucontext)
@@ -787,9 +836,9 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file,
 
        idr_remove(&ib_uverbs_cq_idr, cmd.cq_handle);
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_del(&uobj->uobject.list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
        ib_uverbs_release_ucq(file, ev_file, uobj);
 
@@ -803,7 +852,7 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file,
                ret = -EFAULT;
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret ? ret : in_len;
 }
@@ -815,7 +864,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
        struct ib_uverbs_create_qp      cmd;
        struct ib_uverbs_create_qp_resp resp;
        struct ib_udata                 udata;
-       struct ib_uevent_object        *uobj;
+       struct ib_uqp_object           *uobj;
        struct ib_pd                   *pd;
        struct ib_cq                   *scq, *rcq;
        struct ib_srq                  *srq;
@@ -837,7 +886,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
        if (!uobj)
                return -ENOMEM;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        pd  = idr_find(&ib_uverbs_pd_idr, cmd.pd_handle);
        scq = idr_find(&ib_uverbs_cq_idr, cmd.send_cq_handle);
@@ -866,10 +915,11 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
        attr.cap.max_recv_sge    = cmd.max_recv_sge;
        attr.cap.max_inline_data = cmd.max_inline_data;
 
-       uobj->uobject.user_handle = cmd.user_handle;
-       uobj->uobject.context     = file->ucontext;
-       uobj->events_reported     = 0;
-       INIT_LIST_HEAD(&uobj->event_list);
+       uobj->uevent.uobject.user_handle = cmd.user_handle;
+       uobj->uevent.uobject.context     = file->ucontext;
+       uobj->uevent.events_reported     = 0;
+       INIT_LIST_HEAD(&uobj->uevent.event_list);
+       INIT_LIST_HEAD(&uobj->mcast_list);
 
        qp = pd->device->create_qp(pd, &attr, &udata);
        if (IS_ERR(qp)) {
@@ -882,7 +932,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
        qp->send_cq       = attr.send_cq;
        qp->recv_cq       = attr.recv_cq;
        qp->srq           = attr.srq;
-       qp->uobject       = &uobj->uobject;
+       qp->uobject       = &uobj->uevent.uobject;
        qp->event_handler = attr.event_handler;
        qp->qp_context    = attr.qp_context;
        qp->qp_type       = attr.qp_type;
@@ -901,14 +951,14 @@ retry:
                goto err_destroy;
        }
 
-       ret = idr_get_new(&ib_uverbs_qp_idr, qp, &uobj->uobject.id);
+       ret = idr_get_new(&ib_uverbs_qp_idr, qp, &uobj->uevent.uobject.id);
 
        if (ret == -EAGAIN)
                goto retry;
        if (ret)
                goto err_destroy;
 
-       resp.qp_handle       = uobj->uobject.id;
+       resp.qp_handle       = uobj->uevent.uobject.id;
        resp.max_recv_sge    = attr.cap.max_recv_sge;
        resp.max_send_sge    = attr.cap.max_send_sge;
        resp.max_recv_wr     = attr.cap.max_recv_wr;
@@ -921,27 +971,132 @@ retry:
                goto err_idr;
        }
 
-       down(&file->mutex);
-       list_add_tail(&uobj->uobject.list, &file->ucontext->qp_list);
-       up(&file->mutex);
+       mutex_lock(&file->mutex);
+       list_add_tail(&uobj->uevent.uobject.list, &file->ucontext->qp_list);
+       mutex_unlock(&file->mutex);
 
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return in_len;
 
 err_idr:
-       idr_remove(&ib_uverbs_qp_idr, uobj->uobject.id);
+       idr_remove(&ib_uverbs_qp_idr, uobj->uevent.uobject.id);
 
 err_destroy:
        ib_destroy_qp(qp);
+       atomic_dec(&pd->usecnt);
+       atomic_dec(&attr.send_cq->usecnt);
+       atomic_dec(&attr.recv_cq->usecnt);
+       if (attr.srq)
+               atomic_dec(&attr.srq->usecnt);
 
 err_up:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        kfree(uobj);
        return ret;
 }
 
+ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,
+                          const char __user *buf, int in_len,
+                          int out_len)
+{
+       struct ib_uverbs_query_qp      cmd;
+       struct ib_uverbs_query_qp_resp resp;
+       struct ib_qp                   *qp;
+       struct ib_qp_attr              *attr;
+       struct ib_qp_init_attr         *init_attr;
+       int                            ret;
+
+       if (copy_from_user(&cmd, buf, sizeof cmd))
+               return -EFAULT;
+
+       attr      = kmalloc(sizeof *attr, GFP_KERNEL);
+       init_attr = kmalloc(sizeof *init_attr, GFP_KERNEL);
+       if (!attr || !init_attr) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       mutex_lock(&ib_uverbs_idr_mutex);
+
+       qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
+       if (qp && qp->uobject->context == file->ucontext)
+               ret = ib_query_qp(qp, attr, cmd.attr_mask, init_attr);
+       else
+               ret = -EINVAL;
+
+       mutex_unlock(&ib_uverbs_idr_mutex);
+
+       if (ret)
+               goto out;
+
+       memset(&resp, 0, sizeof resp);
+
+       resp.qp_state               = attr->qp_state;
+       resp.cur_qp_state           = attr->cur_qp_state;
+       resp.path_mtu               = attr->path_mtu;
+       resp.path_mig_state         = attr->path_mig_state;
+       resp.qkey                   = attr->qkey;
+       resp.rq_psn                 = attr->rq_psn;
+       resp.sq_psn                 = attr->sq_psn;
+       resp.dest_qp_num            = attr->dest_qp_num;
+       resp.qp_access_flags        = attr->qp_access_flags;
+       resp.pkey_index             = attr->pkey_index;
+       resp.alt_pkey_index         = attr->alt_pkey_index;
+       resp.en_sqd_async_notify    = attr->en_sqd_async_notify;
+       resp.max_rd_atomic          = attr->max_rd_atomic;
+       resp.max_dest_rd_atomic     = attr->max_dest_rd_atomic;
+       resp.min_rnr_timer          = attr->min_rnr_timer;
+       resp.port_num               = attr->port_num;
+       resp.timeout                = attr->timeout;
+       resp.retry_cnt              = attr->retry_cnt;
+       resp.rnr_retry              = attr->rnr_retry;
+       resp.alt_port_num           = attr->alt_port_num;
+       resp.alt_timeout            = attr->alt_timeout;
+
+       memcpy(resp.dest.dgid, attr->ah_attr.grh.dgid.raw, 16);
+       resp.dest.flow_label        = attr->ah_attr.grh.flow_label;
+       resp.dest.sgid_index        = attr->ah_attr.grh.sgid_index;
+       resp.dest.hop_limit         = attr->ah_attr.grh.hop_limit;
+       resp.dest.traffic_class     = attr->ah_attr.grh.traffic_class;
+       resp.dest.dlid              = attr->ah_attr.dlid;
+       resp.dest.sl                = attr->ah_attr.sl;
+       resp.dest.src_path_bits     = attr->ah_attr.src_path_bits;
+       resp.dest.static_rate       = attr->ah_attr.static_rate;
+       resp.dest.is_global         = !!(attr->ah_attr.ah_flags & IB_AH_GRH);
+       resp.dest.port_num          = attr->ah_attr.port_num;
+
+       memcpy(resp.alt_dest.dgid, attr->alt_ah_attr.grh.dgid.raw, 16);
+       resp.alt_dest.flow_label    = attr->alt_ah_attr.grh.flow_label;
+       resp.alt_dest.sgid_index    = attr->alt_ah_attr.grh.sgid_index;
+       resp.alt_dest.hop_limit     = attr->alt_ah_attr.grh.hop_limit;
+       resp.alt_dest.traffic_class = attr->alt_ah_attr.grh.traffic_class;
+       resp.alt_dest.dlid          = attr->alt_ah_attr.dlid;
+       resp.alt_dest.sl            = attr->alt_ah_attr.sl;
+       resp.alt_dest.src_path_bits = attr->alt_ah_attr.src_path_bits;
+       resp.alt_dest.static_rate   = attr->alt_ah_attr.static_rate;
+       resp.alt_dest.is_global     = !!(attr->alt_ah_attr.ah_flags & IB_AH_GRH);
+       resp.alt_dest.port_num      = attr->alt_ah_attr.port_num;
+
+       resp.max_send_wr            = init_attr->cap.max_send_wr;
+       resp.max_recv_wr            = init_attr->cap.max_recv_wr;
+       resp.max_send_sge           = init_attr->cap.max_send_sge;
+       resp.max_recv_sge           = init_attr->cap.max_recv_sge;
+       resp.max_inline_data        = init_attr->cap.max_inline_data;
+       resp.sq_sig_all             = init_attr->sq_sig_type == IB_SIGNAL_ALL_WR;
+
+       if (copy_to_user((void __user *) (unsigned long) cmd.response,
+                        &resp, sizeof resp))
+               ret = -EFAULT;
+
+out:
+       kfree(attr);
+       kfree(init_attr);
+
+       return ret ? ret : in_len;
+}
+
 ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
                            const char __user *buf, int in_len,
                            int out_len)
@@ -958,7 +1113,7 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
        if (!attr)
                return -ENOMEM;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
        if (!qp || qp->uobject->context != file->ucontext) {
@@ -976,7 +1131,7 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
        attr->dest_qp_num         = cmd.dest_qp_num;
        attr->qp_access_flags     = cmd.qp_access_flags;
        attr->pkey_index          = cmd.pkey_index;
-       attr->alt_pkey_index      = cmd.pkey_index;
+       attr->alt_pkey_index      = cmd.alt_pkey_index;
        attr->en_sqd_async_notify = cmd.en_sqd_async_notify;
        attr->max_rd_atomic       = cmd.max_rd_atomic;
        attr->max_dest_rd_atomic  = cmd.max_dest_rd_atomic;
@@ -1019,7 +1174,7 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,
        ret = in_len;
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
        kfree(attr);
 
        return ret;
@@ -1032,7 +1187,7 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
        struct ib_uverbs_destroy_qp      cmd;
        struct ib_uverbs_destroy_qp_resp resp;
        struct ib_qp                    *qp;
-       struct ib_uevent_object         *uobj;
+       struct ib_uqp_object            *uobj;
        int                              ret = -EINVAL;
 
        if (copy_from_user(&cmd, buf, sizeof cmd))
@@ -1040,13 +1195,18 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
 
        memset(&resp, 0, sizeof resp);
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
        if (!qp || qp->uobject->context != file->ucontext)
                goto out;
 
-       uobj = container_of(qp->uobject, struct ib_uevent_object, uobject);
+       uobj = container_of(qp->uobject, struct ib_uqp_object, uevent.uobject);
+
+       if (!list_empty(&uobj->mcast_list)) {
+               ret = -EBUSY;
+               goto out;
+       }
 
        ret = ib_destroy_qp(qp);
        if (ret)
@@ -1054,13 +1214,13 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
 
        idr_remove(&ib_uverbs_qp_idr, cmd.qp_handle);
 
-       down(&file->mutex);
-       list_del(&uobj->uobject.list);
-       up(&file->mutex);
+       mutex_lock(&file->mutex);
+       list_del(&uobj->uevent.uobject.list);
+       mutex_unlock(&file->mutex);
 
-       ib_uverbs_release_uevent(file, uobj);
+       ib_uverbs_release_uevent(file, &uobj->uevent);
 
-       resp.events_reported = uobj->events_reported;
+       resp.events_reported = uobj->uevent.events_reported;
 
        kfree(uobj);
 
@@ -1069,14 +1229,14 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
                ret = -EFAULT;
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret ? ret : in_len;
 }
 
 ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
-                            const char __user *buf, int in_len,
-                            int out_len)
+                           const char __user *buf, int in_len,
+                           int out_len)
 {
        struct ib_uverbs_post_send      cmd;
        struct ib_uverbs_post_send_resp resp;
@@ -1100,7 +1260,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
        if (!user_wr)
                return -ENOMEM;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
        if (!qp || qp->uobject->context != file->ucontext)
@@ -1205,7 +1365,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
                ret = -EFAULT;
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        while (wr) {
                next = wr->next;
@@ -1304,8 +1464,8 @@ err:
 }
 
 ssize_t ib_uverbs_post_recv(struct ib_uverbs_file *file,
-                            const char __user *buf, int in_len,
-                            int out_len)
+                           const char __user *buf, int in_len,
+                           int out_len)
 {
        struct ib_uverbs_post_recv      cmd;
        struct ib_uverbs_post_recv_resp resp;
@@ -1322,7 +1482,7 @@ ssize_t ib_uverbs_post_recv(struct ib_uverbs_file *file,
        if (IS_ERR(wr))
                return PTR_ERR(wr);
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
        if (!qp || qp->uobject->context != file->ucontext)
@@ -1343,7 +1503,7 @@ ssize_t ib_uverbs_post_recv(struct ib_uverbs_file *file,
                ret = -EFAULT;
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        while (wr) {
                next = wr->next;
@@ -1355,8 +1515,8 @@ out:
 }
 
 ssize_t ib_uverbs_post_srq_recv(struct ib_uverbs_file *file,
-                            const char __user *buf, int in_len,
-                            int out_len)
+                               const char __user *buf, int in_len,
+                               int out_len)
 {
        struct ib_uverbs_post_srq_recv      cmd;
        struct ib_uverbs_post_srq_recv_resp resp;
@@ -1373,7 +1533,7 @@ ssize_t ib_uverbs_post_srq_recv(struct ib_uverbs_file *file,
        if (IS_ERR(wr))
                return PTR_ERR(wr);
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        srq = idr_find(&ib_uverbs_srq_idr, cmd.srq_handle);
        if (!srq || srq->uobject->context != file->ucontext)
@@ -1394,7 +1554,7 @@ ssize_t ib_uverbs_post_srq_recv(struct ib_uverbs_file *file,
                ret = -EFAULT;
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        while (wr) {
                next = wr->next;
@@ -1427,7 +1587,7 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
        if (!uobj)
                return -ENOMEM;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        pd = idr_find(&ib_uverbs_pd_idr, cmd.pd_handle);
        if (!pd || pd->uobject->context != file->ucontext) {
@@ -1442,6 +1602,7 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,
        attr.sl                = cmd.attr.sl;
        attr.src_path_bits     = cmd.attr.src_path_bits;
        attr.static_rate       = cmd.attr.static_rate;
+       attr.ah_flags          = cmd.attr.is_global ? IB_AH_GRH : 0;
        attr.port_num          = cmd.attr.port_num;
        attr.grh.flow_label    = cmd.attr.grh.flow_label;
        attr.grh.sgid_index    = cmd.attr.grh.sgid_index;
@@ -1478,11 +1639,11 @@ retry:
                goto err_idr;
        }
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_add_tail(&uobj->list, &file->ucontext->ah_list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return in_len;
 
@@ -1493,7 +1654,7 @@ err_destroy:
        ib_destroy_ah(ah);
 
 err_up:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        kfree(uobj);
        return ret;
@@ -1510,7 +1671,7 @@ ssize_t ib_uverbs_destroy_ah(struct ib_uverbs_file *file,
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        ah = idr_find(&ib_uverbs_ah_idr, cmd.ah_handle);
        if (!ah || ah->uobject->context != file->ucontext)
@@ -1524,14 +1685,14 @@ ssize_t ib_uverbs_destroy_ah(struct ib_uverbs_file *file,
 
        idr_remove(&ib_uverbs_ah_idr, cmd.ah_handle);
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_del(&uobj->list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
        kfree(uobj);
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret ? ret : in_len;
 }
@@ -1542,18 +1703,47 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file,
 {
        struct ib_uverbs_attach_mcast cmd;
        struct ib_qp                 *qp;
+       struct ib_uqp_object         *uobj;
+       struct ib_uverbs_mcast_entry *mcast;
        int                           ret = -EINVAL;
 
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
-       if (qp && qp->uobject->context == file->ucontext)
-               ret = ib_attach_mcast(qp, (union ib_gid *) cmd.gid, cmd.mlid);
+       if (!qp || qp->uobject->context != file->ucontext)
+               goto out;
+
+       uobj = container_of(qp->uobject, struct ib_uqp_object, uevent.uobject);
+
+       list_for_each_entry(mcast, &uobj->mcast_list, list)
+               if (cmd.mlid == mcast->lid &&
+                   !memcmp(cmd.gid, mcast->gid.raw, sizeof mcast->gid.raw)) {
+                       ret = 0;
+                       goto out;
+               }
+
+       mcast = kmalloc(sizeof *mcast, GFP_KERNEL);
+       if (!mcast) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       mcast->lid = cmd.mlid;
+       memcpy(mcast->gid.raw, cmd.gid, sizeof mcast->gid.raw);
 
-       up(&ib_uverbs_idr_mutex);
+       ret = ib_attach_mcast(qp, &mcast->gid, cmd.mlid);
+       if (!ret) {
+               uobj = container_of(qp->uobject, struct ib_uqp_object,
+                                   uevent.uobject);
+               list_add_tail(&mcast->list, &uobj->mcast_list);
+       } else
+               kfree(mcast);
+
+out:
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret ? ret : in_len;
 }
@@ -1563,19 +1753,36 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,
                               int out_len)
 {
        struct ib_uverbs_detach_mcast cmd;
+       struct ib_uqp_object         *uobj;
        struct ib_qp                 *qp;
+       struct ib_uverbs_mcast_entry *mcast;
        int                           ret = -EINVAL;
 
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
-       if (qp && qp->uobject->context == file->ucontext)
-               ret = ib_detach_mcast(qp, (union ib_gid *) cmd.gid, cmd.mlid);
+       if (!qp || qp->uobject->context != file->ucontext)
+               goto out;
 
-       up(&ib_uverbs_idr_mutex);
+       ret = ib_detach_mcast(qp, (union ib_gid *) cmd.gid, cmd.mlid);
+       if (ret)
+               goto out;
+
+       uobj = container_of(qp->uobject, struct ib_uqp_object, uevent.uobject);
+
+       list_for_each_entry(mcast, &uobj->mcast_list, list)
+               if (cmd.mlid == mcast->lid &&
+                   !memcmp(cmd.gid, mcast->gid.raw, sizeof mcast->gid.raw)) {
+                       list_del(&mcast->list);
+                       kfree(mcast);
+                       break;
+               }
+
+out:
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret ? ret : in_len;
 }
@@ -1607,7 +1814,7 @@ ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file,
        if (!uobj)
                return -ENOMEM;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        pd  = idr_find(&ib_uverbs_pd_idr, cmd.pd_handle);
 
@@ -1657,6 +1864,8 @@ retry:
                goto err_destroy;
 
        resp.srq_handle = uobj->uobject.id;
+       resp.max_wr     = attr.attr.max_wr;
+       resp.max_sge    = attr.attr.max_sge;
 
        if (copy_to_user((void __user *) (unsigned long) cmd.response,
                         &resp, sizeof resp)) {
@@ -1664,11 +1873,11 @@ retry:
                goto err_idr;
        }
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_add_tail(&uobj->uobject.list, &file->ucontext->srq_list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return in_len;
 
@@ -1677,9 +1886,10 @@ err_idr:
 
 err_destroy:
        ib_destroy_srq(srq);
+       atomic_dec(&pd->usecnt);
 
 err_up:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        kfree(uobj);
        return ret;
@@ -1697,7 +1907,7 @@ ssize_t ib_uverbs_modify_srq(struct ib_uverbs_file *file,
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        srq = idr_find(&ib_uverbs_srq_idr, cmd.srq_handle);
        if (!srq || srq->uobject->context != file->ucontext) {
@@ -1711,8 +1921,51 @@ ssize_t ib_uverbs_modify_srq(struct ib_uverbs_file *file,
        ret = ib_modify_srq(srq, &attr, cmd.attr_mask);
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
+
+       return ret ? ret : in_len;
+}
 
+ssize_t ib_uverbs_query_srq(struct ib_uverbs_file *file,
+                           const char __user *buf,
+                           int in_len, int out_len)
+{
+       struct ib_uverbs_query_srq      cmd;
+       struct ib_uverbs_query_srq_resp resp;
+       struct ib_srq_attr              attr;
+       struct ib_srq                   *srq;
+       int                             ret;
+
+       if (out_len < sizeof resp)
+               return -ENOSPC;
+
+       if (copy_from_user(&cmd, buf, sizeof cmd))
+               return -EFAULT;
+
+       mutex_lock(&ib_uverbs_idr_mutex);
+
+       srq = idr_find(&ib_uverbs_srq_idr, cmd.srq_handle);
+       if (srq && srq->uobject->context == file->ucontext)
+               ret = ib_query_srq(srq, &attr);
+       else
+               ret = -EINVAL;
+
+       mutex_unlock(&ib_uverbs_idr_mutex);
+
+       if (ret)
+               goto out;
+
+       memset(&resp, 0, sizeof resp);
+
+       resp.max_wr    = attr.max_wr;
+       resp.max_sge   = attr.max_sge;
+       resp.srq_limit = attr.srq_limit;
+
+       if (copy_to_user((void __user *) (unsigned long) cmd.response,
+                        &resp, sizeof resp))
+               ret = -EFAULT;
+
+out:
        return ret ? ret : in_len;
 }
 
@@ -1729,7 +1982,7 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       down(&ib_uverbs_idr_mutex);
+       mutex_lock(&ib_uverbs_idr_mutex);
 
        memset(&resp, 0, sizeof resp);
 
@@ -1745,9 +1998,9 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,
 
        idr_remove(&ib_uverbs_srq_idr, cmd.srq_handle);
 
-       down(&file->mutex);
+       mutex_lock(&file->mutex);
        list_del(&uobj->uobject.list);
-       up(&file->mutex);
+       mutex_unlock(&file->mutex);
 
        ib_uverbs_release_uevent(file, uobj);
 
@@ -1760,7 +2013,7 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,
                ret = -EFAULT;
 
 out:
-       up(&ib_uverbs_idr_mutex);
+       mutex_unlock(&ib_uverbs_idr_mutex);
 
        return ret ? ret : in_len;
 }