Commit f4e40156 authored by Jack Morgenstein's avatar Jack Morgenstein Committed by Roland Dreier

IB/uverbs: track multicast group membership for userspace QPs

uverbs needs to track which multicast groups is each qp
attached to, in order to properly detach when cleanup
is performed on device file close.
Signed-off-by: default avatarJack Morgenstein <jackm@mellanox.co.il>
Signed-off-by: default avatarMichael S. Tsirkin <mst@mellanox.co.il>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent e0ae9ecf
...@@ -105,12 +105,23 @@ struct ib_uverbs_event { ...@@ -105,12 +105,23 @@ struct ib_uverbs_event {
u32 *counter; u32 *counter;
}; };
struct ib_uverbs_mcast_entry {
struct list_head list;
union ib_gid gid;
u16 lid;
};
struct ib_uevent_object { struct ib_uevent_object {
struct ib_uobject uobject; struct ib_uobject uobject;
struct list_head event_list; struct list_head event_list;
u32 events_reported; u32 events_reported;
}; };
struct ib_uqp_object {
struct ib_uevent_object uevent;
struct list_head mcast_list;
};
struct ib_ucq_object { struct ib_ucq_object {
struct ib_uobject uobject; struct ib_uobject uobject;
struct ib_uverbs_file *uverbs_file; struct ib_uverbs_file *uverbs_file;
......
...@@ -815,7 +815,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file, ...@@ -815,7 +815,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
struct ib_uverbs_create_qp cmd; struct ib_uverbs_create_qp cmd;
struct ib_uverbs_create_qp_resp resp; struct ib_uverbs_create_qp_resp resp;
struct ib_udata udata; struct ib_udata udata;
struct ib_uevent_object *uobj; struct ib_uqp_object *uobj;
struct ib_pd *pd; struct ib_pd *pd;
struct ib_cq *scq, *rcq; struct ib_cq *scq, *rcq;
struct ib_srq *srq; struct ib_srq *srq;
...@@ -866,10 +866,11 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file, ...@@ -866,10 +866,11 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
attr.cap.max_recv_sge = cmd.max_recv_sge; attr.cap.max_recv_sge = cmd.max_recv_sge;
attr.cap.max_inline_data = cmd.max_inline_data; attr.cap.max_inline_data = cmd.max_inline_data;
uobj->uobject.user_handle = cmd.user_handle; uobj->uevent.uobject.user_handle = cmd.user_handle;
uobj->uobject.context = file->ucontext; uobj->uevent.uobject.context = file->ucontext;
uobj->events_reported = 0; uobj->uevent.events_reported = 0;
INIT_LIST_HEAD(&uobj->event_list); INIT_LIST_HEAD(&uobj->uevent.event_list);
INIT_LIST_HEAD(&uobj->mcast_list);
qp = pd->device->create_qp(pd, &attr, &udata); qp = pd->device->create_qp(pd, &attr, &udata);
if (IS_ERR(qp)) { if (IS_ERR(qp)) {
...@@ -882,7 +883,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file, ...@@ -882,7 +883,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,
qp->send_cq = attr.send_cq; qp->send_cq = attr.send_cq;
qp->recv_cq = attr.recv_cq; qp->recv_cq = attr.recv_cq;
qp->srq = attr.srq; qp->srq = attr.srq;
qp->uobject = &uobj->uobject; qp->uobject = &uobj->uevent.uobject;
qp->event_handler = attr.event_handler; qp->event_handler = attr.event_handler;
qp->qp_context = attr.qp_context; qp->qp_context = attr.qp_context;
qp->qp_type = attr.qp_type; qp->qp_type = attr.qp_type;
...@@ -901,14 +902,14 @@ retry: ...@@ -901,14 +902,14 @@ retry:
goto err_destroy; 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) if (ret == -EAGAIN)
goto retry; goto retry;
if (ret) if (ret)
goto err_destroy; 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_recv_sge = attr.cap.max_recv_sge;
resp.max_send_sge = attr.cap.max_send_sge; resp.max_send_sge = attr.cap.max_send_sge;
resp.max_recv_wr = attr.cap.max_recv_wr; resp.max_recv_wr = attr.cap.max_recv_wr;
...@@ -922,7 +923,7 @@ retry: ...@@ -922,7 +923,7 @@ retry:
} }
down(&file->mutex); down(&file->mutex);
list_add_tail(&uobj->uobject.list, &file->ucontext->qp_list); list_add_tail(&uobj->uevent.uobject.list, &file->ucontext->qp_list);
up(&file->mutex); up(&file->mutex);
up(&ib_uverbs_idr_mutex); up(&ib_uverbs_idr_mutex);
...@@ -930,7 +931,7 @@ retry: ...@@ -930,7 +931,7 @@ retry:
return in_len; return in_len;
err_idr: err_idr:
idr_remove(&ib_uverbs_qp_idr, uobj->uobject.id); idr_remove(&ib_uverbs_qp_idr, uobj->uevent.uobject.id);
err_destroy: err_destroy:
ib_destroy_qp(qp); ib_destroy_qp(qp);
...@@ -1032,7 +1033,7 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file, ...@@ -1032,7 +1033,7 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
struct ib_uverbs_destroy_qp cmd; struct ib_uverbs_destroy_qp cmd;
struct ib_uverbs_destroy_qp_resp resp; struct ib_uverbs_destroy_qp_resp resp;
struct ib_qp *qp; struct ib_qp *qp;
struct ib_uevent_object *uobj; struct ib_uqp_object *uobj;
int ret = -EINVAL; int ret = -EINVAL;
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
...@@ -1046,7 +1047,12 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file, ...@@ -1046,7 +1047,12 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
if (!qp || qp->uobject->context != file->ucontext) if (!qp || qp->uobject->context != file->ucontext)
goto out; 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); ret = ib_destroy_qp(qp);
if (ret) if (ret)
...@@ -1055,12 +1061,12 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file, ...@@ -1055,12 +1061,12 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
idr_remove(&ib_uverbs_qp_idr, cmd.qp_handle); idr_remove(&ib_uverbs_qp_idr, cmd.qp_handle);
down(&file->mutex); down(&file->mutex);
list_del(&uobj->uobject.list); list_del(&uobj->uevent.uobject.list);
up(&file->mutex); up(&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); kfree(uobj);
...@@ -1542,6 +1548,8 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file, ...@@ -1542,6 +1548,8 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file,
{ {
struct ib_uverbs_attach_mcast cmd; struct ib_uverbs_attach_mcast cmd;
struct ib_qp *qp; struct ib_qp *qp;
struct ib_uqp_object *uobj;
struct ib_uverbs_mcast_entry *mcast;
int ret = -EINVAL; int ret = -EINVAL;
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
...@@ -1550,9 +1558,36 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file, ...@@ -1550,9 +1558,36 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file,
down(&ib_uverbs_idr_mutex); down(&ib_uverbs_idr_mutex);
qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle); qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
if (qp && qp->uobject->context == file->ucontext) if (!qp || qp->uobject->context != file->ucontext)
ret = ib_attach_mcast(qp, (union ib_gid *) cmd.gid, cmd.mlid); 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);
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:
up(&ib_uverbs_idr_mutex); up(&ib_uverbs_idr_mutex);
return ret ? ret : in_len; return ret ? ret : in_len;
...@@ -1563,7 +1598,9 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file, ...@@ -1563,7 +1598,9 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,
int out_len) int out_len)
{ {
struct ib_uverbs_detach_mcast cmd; struct ib_uverbs_detach_mcast cmd;
struct ib_uqp_object *uobj;
struct ib_qp *qp; struct ib_qp *qp;
struct ib_uverbs_mcast_entry *mcast;
int ret = -EINVAL; int ret = -EINVAL;
if (copy_from_user(&cmd, buf, sizeof cmd)) if (copy_from_user(&cmd, buf, sizeof cmd))
...@@ -1572,9 +1609,24 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file, ...@@ -1572,9 +1609,24 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,
down(&ib_uverbs_idr_mutex); down(&ib_uverbs_idr_mutex);
qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle); qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle);
if (qp && qp->uobject->context == file->ucontext) if (!qp || qp->uobject->context != file->ucontext)
ret = ib_detach_mcast(qp, (union ib_gid *) cmd.gid, cmd.mlid); goto out;
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:
up(&ib_uverbs_idr_mutex); up(&ib_uverbs_idr_mutex);
return ret ? ret : in_len; return ret ? ret : in_len;
......
...@@ -160,6 +160,18 @@ void ib_uverbs_release_uevent(struct ib_uverbs_file *file, ...@@ -160,6 +160,18 @@ void ib_uverbs_release_uevent(struct ib_uverbs_file *file,
spin_unlock_irq(&file->async_file->lock); spin_unlock_irq(&file->async_file->lock);
} }
static void ib_uverbs_detach_umcast(struct ib_qp *qp,
struct ib_uqp_object *uobj)
{
struct ib_uverbs_mcast_entry *mcast, *tmp;
list_for_each_entry_safe(mcast, tmp, &uobj->mcast_list, list) {
ib_detach_mcast(qp, &mcast->gid, mcast->lid);
list_del(&mcast->list);
kfree(mcast);
}
}
static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
struct ib_ucontext *context) struct ib_ucontext *context)
{ {
...@@ -180,13 +192,14 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, ...@@ -180,13 +192,14 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
list_for_each_entry_safe(uobj, tmp, &context->qp_list, list) { list_for_each_entry_safe(uobj, tmp, &context->qp_list, list) {
struct ib_qp *qp = idr_find(&ib_uverbs_qp_idr, uobj->id); struct ib_qp *qp = idr_find(&ib_uverbs_qp_idr, uobj->id);
struct ib_uevent_object *uevent = struct ib_uqp_object *uqp =
container_of(uobj, struct ib_uevent_object, uobject); container_of(uobj, struct ib_uqp_object, uevent.uobject);
idr_remove(&ib_uverbs_qp_idr, uobj->id); idr_remove(&ib_uverbs_qp_idr, uobj->id);
ib_uverbs_detach_umcast(qp, uqp);
ib_destroy_qp(qp); ib_destroy_qp(qp);
list_del(&uobj->list); list_del(&uobj->list);
ib_uverbs_release_uevent(file, uevent); ib_uverbs_release_uevent(file, &uqp->uevent);
kfree(uevent); kfree(uqp);
} }
list_for_each_entry_safe(uobj, tmp, &context->cq_list, list) { list_for_each_entry_safe(uobj, tmp, &context->cq_list, list) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment