diff options
author | Jason Gunthorpe <jgg@mellanox.com> | 2018-08-10 05:14:37 +0300 |
---|---|---|
committer | Jason Gunthorpe <jgg@mellanox.com> | 2018-08-11 01:06:24 +0300 |
commit | 6b0d08f4a27134e6fb49aa33ceb53356081bc92e (patch) | |
tree | 7781178fde4ef9aeb01acf736274de2fca581a5a /drivers/infiniband | |
parent | 9ed3e5f447723a41de6bcc29633e9f7e6246d2f7 (diff) | |
download | linux-6b0d08f4a27134e6fb49aa33ceb53356081bc92e.tar.xz |
IB/uverbs: Use uverbs_api to manage the object type inside the uobject
Currently the struct uverbs_obj_type stored in the ib_uobject is part of
the .rodata segment of the module that defines the object. This is a
problem if drivers define new uapi objects as we will be left with a
dangling pointer after device disassociation.
Switch the uverbs_obj_type for struct uverbs_api_object, which is
allocated memory that is part of the uverbs_api and is guaranteed to
always exist. Further this moves the 'type_class' into this memory which
means access to the IDR/FD function pointers is also guaranteed. Drivers
cannot define new types.
This makes it safe to continue to use all uobjects, including driver
defined ones, after disassociation.
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/core/rdma_core.c | 100 | ||||
-rw-r--r-- | drivers/infiniband/core/rdma_core.h | 2 | ||||
-rw-r--r-- | drivers/infiniband/core/uverbs_ioctl.c | 6 |
3 files changed, 57 insertions, 51 deletions
diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c index 4235b9ddc2ad..2814228ead39 100644 --- a/drivers/infiniband/core/rdma_core.c +++ b/drivers/infiniband/core/rdma_core.c @@ -97,7 +97,7 @@ static void uverbs_uobject_free(struct kref *ref) struct ib_uobject *uobj = container_of(ref, struct ib_uobject, ref); - if (uobj->type->type_class->needs_kfree_rcu) + if (uobj->uapi_object->type_class->needs_kfree_rcu) kfree_rcu(uobj, rcu); else kfree(uobj); @@ -180,7 +180,7 @@ static int uverbs_destroy_uobject(struct ib_uobject *uobj, assert_uverbs_usecnt(uobj, UVERBS_LOOKUP_WRITE); if (uobj->object) { - ret = uobj->type->type_class->destroy_hw(uobj, reason); + ret = uobj->uapi_object->type_class->destroy_hw(uobj, reason); if (ret) { if (ib_is_destroy_retryable(ret, reason, uobj)) return ret; @@ -197,7 +197,7 @@ static int uverbs_destroy_uobject(struct ib_uobject *uobj, if (reason == RDMA_REMOVE_ABORT) { WARN_ON(!list_empty(&uobj->list)); WARN_ON(!uobj->context); - uobj->type->type_class->alloc_abort(uobj); + uobj->uapi_object->type_class->alloc_abort(uobj); } uobj->context = NULL; @@ -210,7 +210,7 @@ static int uverbs_destroy_uobject(struct ib_uobject *uobj, if (reason != RDMA_REMOVE_DESTROY) atomic_set(&uobj->usecnt, 0); else - uobj->type->type_class->remove_handle(uobj); + uobj->uapi_object->type_class->remove_handle(uobj); if (!list_empty(&uobj->list)) { spin_lock_irqsave(&ufile->uobjects_lock, flags); @@ -268,13 +268,13 @@ out_unlock: * with a NULL object pointer. The caller must pair this with * uverbs_put_destroy. */ -struct ib_uobject *__uobj_get_destroy(const struct uverbs_obj_type *type, +struct ib_uobject *__uobj_get_destroy(const struct uverbs_api_object *obj, u32 id, struct ib_uverbs_file *ufile) { struct ib_uobject *uobj; int ret; - uobj = rdma_lookup_get_uobject(type, ufile, id, UVERBS_LOOKUP_DESTROY); + uobj = rdma_lookup_get_uobject(obj, ufile, id, UVERBS_LOOKUP_DESTROY); if (IS_ERR(uobj)) return uobj; @@ -292,27 +292,22 @@ struct ib_uobject *__uobj_get_destroy(const struct uverbs_obj_type *type, * on success (negative errno on failure). For use by callers that do not need * the uobj. */ -int __uobj_perform_destroy(const struct uverbs_obj_type *type, u32 id, +int __uobj_perform_destroy(const struct uverbs_api_object *obj, u32 id, struct ib_uverbs_file *ufile, int success_res) { struct ib_uobject *uobj; - uobj = __uobj_get_destroy(type, id, ufile); + uobj = __uobj_get_destroy(obj, id, ufile); if (IS_ERR(uobj)) return PTR_ERR(uobj); - /* - * FIXME: After destroy this is not safe. We no longer hold the rwsem - * so disassociation could have completed and unloaded the module that - * backs the uobj->type pointer. - */ rdma_lookup_put_uobject(uobj, UVERBS_LOOKUP_WRITE); return success_res; } /* alloc_uobj must be undone by uverbs_destroy_uobject() */ static struct ib_uobject *alloc_uobj(struct ib_uverbs_file *ufile, - const struct uverbs_obj_type *type) + const struct uverbs_api_object *obj) { struct ib_uobject *uobj; struct ib_ucontext *ucontext; @@ -321,7 +316,7 @@ static struct ib_uobject *alloc_uobj(struct ib_uverbs_file *ufile, if (IS_ERR(ucontext)) return ERR_CAST(ucontext); - uobj = kzalloc(type->obj_size, GFP_KERNEL); + uobj = kzalloc(obj->type_attrs->obj_size, GFP_KERNEL); if (!uobj) return ERR_PTR(-ENOMEM); /* @@ -331,7 +326,7 @@ static struct ib_uobject *alloc_uobj(struct ib_uverbs_file *ufile, uobj->ufile = ufile; uobj->context = ucontext; INIT_LIST_HEAD(&uobj->list); - uobj->type = type; + uobj->uapi_object = obj; /* * Allocated objects start out as write locked to deny any other * syscalls from accessing them until they are committed. See @@ -368,7 +363,7 @@ static int idr_add_uobj(struct ib_uobject *uobj) /* Returns the ib_uobject or an error. The caller should check for IS_ERR. */ static struct ib_uobject * -lookup_get_idr_uobject(const struct uverbs_obj_type *type, +lookup_get_idr_uobject(const struct uverbs_api_object *obj, struct ib_uverbs_file *ufile, s64 id, enum rdma_lookup_mode mode) { @@ -401,15 +396,14 @@ free: } static struct ib_uobject * -lookup_get_fd_uobject(const struct uverbs_obj_type *type, +lookup_get_fd_uobject(const struct uverbs_api_object *obj, struct ib_uverbs_file *ufile, s64 id, enum rdma_lookup_mode mode) { + const struct uverbs_obj_fd_type *fd_type; struct file *f; struct ib_uobject *uobject; int fdno = id; - const struct uverbs_obj_fd_type *fd_type = - container_of(type, struct uverbs_obj_fd_type, type); if (fdno != id) return ERR_PTR(-EINVAL); @@ -417,6 +411,11 @@ lookup_get_fd_uobject(const struct uverbs_obj_type *type, if (mode != UVERBS_LOOKUP_READ) return ERR_PTR(-EOPNOTSUPP); + if (!obj->type_attrs) + return ERR_PTR(-EIO); + fd_type = + container_of(obj->type_attrs, struct uverbs_obj_fd_type, type); + f = fget(fdno); if (!f) return ERR_PTR(-EBADF); @@ -436,18 +435,21 @@ lookup_get_fd_uobject(const struct uverbs_obj_type *type, return uobject; } -struct ib_uobject *rdma_lookup_get_uobject(const struct uverbs_obj_type *type, +struct ib_uobject *rdma_lookup_get_uobject(const struct uverbs_api_object *obj, struct ib_uverbs_file *ufile, s64 id, enum rdma_lookup_mode mode) { struct ib_uobject *uobj; int ret; - uobj = type->type_class->lookup_get(type, ufile, id, mode); + if (!obj) + return ERR_PTR(-EINVAL); + + uobj = obj->type_class->lookup_get(obj, ufile, id, mode); if (IS_ERR(uobj)) return uobj; - if (uobj->type != type) { + if (uobj->uapi_object != obj) { ret = -EINVAL; goto free; } @@ -469,18 +471,19 @@ struct ib_uobject *rdma_lookup_get_uobject(const struct uverbs_obj_type *type, return uobj; free: - uobj->type->type_class->lookup_put(uobj, mode); + obj->type_class->lookup_put(uobj, mode); uverbs_uobject_put(uobj); return ERR_PTR(ret); } -static struct ib_uobject *alloc_begin_idr_uobject(const struct uverbs_obj_type *type, - struct ib_uverbs_file *ufile) +static struct ib_uobject * +alloc_begin_idr_uobject(const struct uverbs_api_object *obj, + struct ib_uverbs_file *ufile) { int ret; struct ib_uobject *uobj; - uobj = alloc_uobj(ufile, type); + uobj = alloc_uobj(ufile, obj); if (IS_ERR(uobj)) return uobj; @@ -504,8 +507,9 @@ uobj_put: return ERR_PTR(ret); } -static struct ib_uobject *alloc_begin_fd_uobject(const struct uverbs_obj_type *type, - struct ib_uverbs_file *ufile) +static struct ib_uobject * +alloc_begin_fd_uobject(const struct uverbs_api_object *obj, + struct ib_uverbs_file *ufile) { int new_fd; struct ib_uobject *uobj; @@ -514,7 +518,7 @@ static struct ib_uobject *alloc_begin_fd_uobject(const struct uverbs_obj_type *t if (new_fd < 0) return ERR_PTR(new_fd); - uobj = alloc_uobj(ufile, type); + uobj = alloc_uobj(ufile, obj); if (IS_ERR(uobj)) { put_unused_fd(new_fd); return uobj; @@ -526,11 +530,14 @@ static struct ib_uobject *alloc_begin_fd_uobject(const struct uverbs_obj_type *t return uobj; } -struct ib_uobject *rdma_alloc_begin_uobject(const struct uverbs_obj_type *type, +struct ib_uobject *rdma_alloc_begin_uobject(const struct uverbs_api_object *obj, struct ib_uverbs_file *ufile) { struct ib_uobject *ret; + if (!obj) + return ERR_PTR(-EINVAL); + /* * The hw_destroy_rwsem is held across the entire object creation and * released during rdma_alloc_commit_uobject or @@ -539,7 +546,7 @@ struct ib_uobject *rdma_alloc_begin_uobject(const struct uverbs_obj_type *type, if (!down_read_trylock(&ufile->hw_destroy_rwsem)) return ERR_PTR(-EIO); - ret = type->type_class->alloc_begin(type, ufile); + ret = obj->type_class->alloc_begin(obj, ufile); if (IS_ERR(ret)) { up_read(&ufile->hw_destroy_rwsem); return ret; @@ -561,8 +568,8 @@ static int __must_check destroy_hw_idr_uobject(struct ib_uobject *uobj, enum rdma_remove_reason why) { const struct uverbs_obj_idr_type *idr_type = - container_of(uobj->type, struct uverbs_obj_idr_type, - type); + container_of(uobj->uapi_object->type_attrs, + struct uverbs_obj_idr_type, type); int ret = idr_type->destroy_object(uobj, why); /* @@ -599,8 +606,8 @@ static void alloc_abort_fd_uobject(struct ib_uobject *uobj) static int __must_check destroy_hw_fd_uobject(struct ib_uobject *uobj, enum rdma_remove_reason why) { - const struct uverbs_obj_fd_type *fd_type = - container_of(uobj->type, struct uverbs_obj_fd_type, type); + const struct uverbs_obj_fd_type *fd_type = container_of( + uobj->uapi_object->type_attrs, struct uverbs_obj_fd_type, type); int ret = fd_type->context_closed(uobj, why); if (ib_is_destroy_retryable(ret, why, uobj)) @@ -633,8 +640,8 @@ static int alloc_commit_idr_uobject(struct ib_uobject *uobj) static int alloc_commit_fd_uobject(struct ib_uobject *uobj) { - const struct uverbs_obj_fd_type *fd_type = - container_of(uobj->type, struct uverbs_obj_fd_type, type); + const struct uverbs_obj_fd_type *fd_type = container_of( + uobj->uapi_object->type_attrs, struct uverbs_obj_fd_type, type); int fd = uobj->id; struct file *filp; @@ -679,7 +686,7 @@ int __must_check rdma_alloc_commit_uobject(struct ib_uobject *uobj) int ret; /* alloc_commit consumes the uobj kref */ - ret = uobj->type->type_class->alloc_commit(uobj); + ret = uobj->uapi_object->type_class->alloc_commit(uobj); if (ret) { uverbs_destroy_uobject(uobj, RDMA_REMOVE_ABORT); up_read(&ufile->hw_destroy_rwsem); @@ -735,7 +742,7 @@ void rdma_lookup_put_uobject(struct ib_uobject *uobj, enum rdma_lookup_mode mode) { assert_uverbs_usecnt(uobj, mode); - uobj->type->type_class->lookup_put(uobj, mode); + uobj->uapi_object->type_class->lookup_put(uobj, mode); /* * In order to unlock an object, either decrease its usecnt for * read access or zero it in case of exclusive access. See @@ -995,23 +1002,26 @@ const struct uverbs_obj_type_class uverbs_fd_class = { EXPORT_SYMBOL(uverbs_fd_class); struct ib_uobject * -uverbs_get_uobject_from_file(const struct uverbs_obj_type *type_attrs, +uverbs_get_uobject_from_file(u16 object_id, struct ib_uverbs_file *ufile, enum uverbs_obj_access access, s64 id) { + const struct uverbs_api_object *obj = + uapi_get_object(ufile->device->uapi, object_id); + switch (access) { case UVERBS_ACCESS_READ: - return rdma_lookup_get_uobject(type_attrs, ufile, id, + return rdma_lookup_get_uobject(obj, ufile, id, UVERBS_LOOKUP_READ); case UVERBS_ACCESS_DESTROY: /* Actual destruction is done inside uverbs_handle_method */ - return rdma_lookup_get_uobject(type_attrs, ufile, id, + return rdma_lookup_get_uobject(obj, ufile, id, UVERBS_LOOKUP_DESTROY); case UVERBS_ACCESS_WRITE: - return rdma_lookup_get_uobject(type_attrs, ufile, id, + return rdma_lookup_get_uobject(obj, ufile, id, UVERBS_LOOKUP_WRITE); case UVERBS_ACCESS_NEW: - return rdma_alloc_begin_uobject(type_attrs, ufile); + return rdma_alloc_begin_uobject(obj, ufile); default: WARN_ON(true); return ERR_PTR(-EOPNOTSUPP); diff --git a/drivers/infiniband/core/rdma_core.h b/drivers/infiniband/core/rdma_core.h index 55a687285b1d..d89569d87b1c 100644 --- a/drivers/infiniband/core/rdma_core.h +++ b/drivers/infiniband/core/rdma_core.h @@ -89,7 +89,7 @@ void uverbs_close_fd(struct file *f); * uverbs_finalize_objects are called. */ struct ib_uobject * -uverbs_get_uobject_from_file(const struct uverbs_obj_type *type_attrs, +uverbs_get_uobject_from_file(u16 object_id, struct ib_uverbs_file *ufile, enum uverbs_obj_access access, s64 id); diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c index 23ff698ab08e..8a052d0fdf2c 100644 --- a/drivers/infiniband/core/uverbs_ioctl.c +++ b/drivers/infiniband/core/uverbs_ioctl.c @@ -57,7 +57,6 @@ static int uverbs_process_attr(struct ib_uverbs_file *ufile, const struct uverbs_attr_spec *spec; const struct uverbs_attr_spec *val_spec; struct uverbs_attr *e; - const struct uverbs_object_spec *object; struct uverbs_obj_attr *o_attr; struct uverbs_attr *elements = attr_bundle_h->attrs; @@ -145,9 +144,6 @@ static int uverbs_process_attr(struct ib_uverbs_file *ufile, return -EINVAL; o_attr = &e->obj_attr; - object = uverbs_get_object(ufile, spec->u.obj.obj_type); - if (!object) - return -EINVAL; /* specs are allowed to have only one destroy attribute */ WARN_ON(spec->u.obj.access == UVERBS_ACCESS_DESTROY && @@ -162,7 +158,7 @@ static int uverbs_process_attr(struct ib_uverbs_file *ufile, * IDR implementation today rejects negative IDs */ o_attr->uobject = uverbs_get_uobject_from_file( - object->type_attrs, + spec->u.obj.obj_type, ufile, spec->u.obj.access, uattr->data_s64); |