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: Jack Morgenstein <jackm@mellanox.co.il>
Signed-off-by: Michael S. Tsirkin <mst@mellanox.co.il>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
Jack Morgenstein 2005-11-29 16:57:01 -08:00 committed by Roland Dreier
parent e0ae9ecf46
commit f4e401562c
3 changed files with 99 additions and 23 deletions

View file

@ -105,12 +105,23 @@ struct ib_uverbs_event {
u32 *counter;
};
struct ib_uverbs_mcast_entry {
struct list_head list;
union ib_gid gid;
u16 lid;
};
struct ib_uevent_object {
struct ib_uobject uobject;
struct list_head event_list;
u32 events_reported;
};
struct ib_uqp_object {
struct ib_uevent_object uevent;
struct list_head mcast_list;
};
struct ib_ucq_object {
struct ib_uobject uobject;
struct ib_uverbs_file *uverbs_file;

View 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_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;
@ -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_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 +883,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 +902,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;
@ -922,7 +923,7 @@ retry:
}
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(&ib_uverbs_idr_mutex);
@ -930,7 +931,7 @@ retry:
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);
@ -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_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))
@ -1046,7 +1047,12 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,
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)
@ -1055,12 +1061,12 @@ 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);
list_del(&uobj->uevent.uobject.list);
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);
@ -1542,6 +1548,8 @@ 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))
@ -1550,9 +1558,36 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file,
down(&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);
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);
return ret ? ret : in_len;
@ -1563,7 +1598,9 @@ 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))
@ -1572,9 +1609,24 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,
down(&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;
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);
return ret ? ret : in_len;

View file

@ -160,6 +160,18 @@ void ib_uverbs_release_uevent(struct ib_uverbs_file *file,
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,
struct ib_ucontext *context)
{
@ -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) {
struct ib_qp *qp = idr_find(&ib_uverbs_qp_idr, uobj->id);
struct ib_uevent_object *uevent =
container_of(uobj, struct ib_uevent_object, uobject);
struct ib_uqp_object *uqp =
container_of(uobj, struct ib_uqp_object, uevent.uobject);
idr_remove(&ib_uverbs_qp_idr, uobj->id);
ib_uverbs_detach_umcast(qp, uqp);
ib_destroy_qp(qp);
list_del(&uobj->list);
ib_uverbs_release_uevent(file, uevent);
kfree(uevent);
ib_uverbs_release_uevent(file, &uqp->uevent);
kfree(uqp);
}
list_for_each_entry_safe(uobj, tmp, &context->cq_list, list) {