Multicast attach/detach operations on a QP are carried under the
readers'/writers' lock of the QP. Such protection is not sufficient since a
reader's lock allows more than one reader to acquire the lock. However, list
manipulation implies write operations on the list variable. Use a spinlock to
provide protection.
We have hit kernel oops when running applications that perform multicast
joins/leaves. This patch solved the issue.

Reported by: Mike Dubman <[email protected]>
Signed-off-by: Eli Cohen <[email protected]>
---
 drivers/infiniband/core/uverbs_cmd.c |   11 ++++++++++-
 include/rdma/ib_verbs.h              |    1 +
 2 files changed, 11 insertions(+), 1 deletions(-)

diff --git a/drivers/infiniband/core/uverbs_cmd.c 
b/drivers/infiniband/core/uverbs_cmd.c
index c426992..0209292 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -1132,6 +1132,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
        if (ret)
                goto err_destroy;
 
+       spin_lock_init(&qp->mcast_lock);
        memset(&resp, 0, sizeof resp);
        resp.qpn             = qp->qp_num;
        resp.qp_handle       = obj->uevent.uobject.id;
@@ -1910,12 +1911,15 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file 
*file,
 
        obj = container_of(qp->uobject, struct ib_uqp_object, uevent.uobject);
 
+       spin_lock(&qp->mcast_lock);
        list_for_each_entry(mcast, &obj->mcast_list, list)
                if (cmd.mlid == mcast->lid &&
                    !memcmp(cmd.gid, mcast->gid.raw, sizeof mcast->gid.raw)) {
                        ret = 0;
+                       spin_unlock(&qp->mcast_lock);
                        goto out_put;
                }
+       spin_unlock(&qp->mcast_lock);
 
        mcast = kmalloc(sizeof *mcast, GFP_KERNEL);
        if (!mcast) {
@@ -1927,8 +1931,11 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file 
*file,
        memcpy(mcast->gid.raw, cmd.gid, sizeof mcast->gid.raw);
 
        ret = ib_attach_mcast(qp, &mcast->gid, cmd.mlid);
-       if (!ret)
+       if (!ret) {
+               spin_lock(&qp->mcast_lock);
                list_add_tail(&mcast->list, &obj->mcast_list);
+               spin_unlock(&qp->mcast_lock);
+       }
        else
                kfree(mcast);
 
@@ -1961,6 +1968,7 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file 
*file,
 
        obj = container_of(qp->uobject, struct ib_uqp_object, uevent.uobject);
 
+       spin_lock(&qp->mcast_lock);
        list_for_each_entry(mcast, &obj->mcast_list, list)
                if (cmd.mlid == mcast->lid &&
                    !memcmp(cmd.gid, mcast->gid.raw, sizeof mcast->gid.raw)) {
@@ -1968,6 +1976,7 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file 
*file,
                        kfree(mcast);
                        break;
                }
+       spin_unlock(&qp->mcast_lock);
 
 out_put:
        put_qp_read(qp);
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 55cd0a0..bf86750 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -895,6 +895,7 @@ struct ib_qp {
        void                   *qp_context;
        u32                     qp_num;
        enum ib_qp_type         qp_type;
+       spinlock_t              mcast_lock;
 };
 
 struct ib_mr {
-- 
1.7.8

--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to