From: Jason Gunthorpe <jgg@xxxxxxxxxxxx> 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@xxxxxxxxxxxx> --- drivers/infiniband/core/rdma_core.c | 100 ++++++++++++++----------- drivers/infiniband/core/rdma_core.h | 2 +- drivers/infiniband/core/uverbs_ioctl.c | 6 +- include/rdma/ib_verbs.h | 2 +- include/rdma/uverbs_std_types.h | 30 ++++---- include/rdma/uverbs_types.h | 9 ++- 6 files changed, 79 insertions(+), 70 deletions(-) diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c index 4235b9ddc2adaf..2814228ead39f0 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 @@ int uobj_destroy(struct ib_uobject *uobj) * 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 @@ lookup_get_idr_uobject(const struct uverbs_obj_type *type, } 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 @@ static struct ib_uobject *alloc_begin_idr_uobject(const struct uverbs_obj_type * 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 55a687285b1daa..d89569d87b1cff 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 f0655a84f9d9c9..8fd64f2b147a56 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); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 3b07201b9a804e..5d404c20b49f27 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1524,7 +1524,7 @@ struct ib_uobject { atomic_t usecnt; /* protects exclusive access */ struct rcu_head rcu; /* kfree_rcu() overhead */ - const struct uverbs_obj_type *type; + const struct uverbs_api_object *uapi_object; }; struct ib_udata { diff --git a/include/rdma/uverbs_std_types.h b/include/rdma/uverbs_std_types.h index 64ee2545dd3db3..3b00231cc084d7 100644 --- a/include/rdma/uverbs_std_types.h +++ b/include/rdma/uverbs_std_types.h @@ -54,14 +54,15 @@ static inline const struct uverbs_object_tree_def *uverbs_default_get_objects(vo */ #define _uobj_check_id(_id) ((_id) * typecheck(u32, _id)) -#define uobj_get_type(_object) UVERBS_OBJECT(_object).type_attrs +#define uobj_get_type(_ufile, _object) \ + uapi_get_object((_ufile)->device->uapi, _object) #define uobj_get_read(_type, _id, _ufile) \ - rdma_lookup_get_uobject(uobj_get_type(_type), _ufile, \ + rdma_lookup_get_uobject(uobj_get_type(_ufile, _type), _ufile, \ _uobj_check_id(_id), UVERBS_LOOKUP_READ) #define ufd_get_read(_type, _fdnum, _ufile) \ - rdma_lookup_get_uobject(uobj_get_type(_type), _ufile, \ + rdma_lookup_get_uobject(uobj_get_type(_ufile, _type), _ufile, \ (_fdnum)*typecheck(s32, _fdnum), \ UVERBS_LOOKUP_READ) @@ -76,20 +77,21 @@ static inline void *_uobj_get_obj_read(struct ib_uobject *uobj) uobj_get_read(_type, _id, _ufile))) #define uobj_get_write(_type, _id, _ufile) \ - rdma_lookup_get_uobject(uobj_get_type(_type), _ufile, \ + rdma_lookup_get_uobject(uobj_get_type(_ufile, _type), _ufile, \ _uobj_check_id(_id), UVERBS_LOOKUP_WRITE) -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); #define uobj_perform_destroy(_type, _id, _ufile, _success_res) \ - __uobj_perform_destroy(uobj_get_type(_type), _uobj_check_id(_id), \ - _ufile, _success_res) + __uobj_perform_destroy(uobj_get_type(_ufile, _type), \ + _uobj_check_id(_id), _ufile, _success_res) -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); #define uobj_get_destroy(_type, _id, _ufile) \ - __uobj_get_destroy(uobj_get_type(_type), _uobj_check_id(_id), _ufile) + __uobj_get_destroy(uobj_get_type(_ufile, _type), _uobj_check_id(_id), \ + _ufile) static inline void uobj_put_destroy(struct ib_uobject *uobj) { @@ -124,11 +126,11 @@ static inline void uobj_alloc_abort(struct ib_uobject *uobj) rdma_alloc_abort_uobject(uobj); } -static inline struct ib_uobject *__uobj_alloc(const struct uverbs_obj_type *type, - struct ib_uverbs_file *ufile, - struct ib_device **ib_dev) +static inline struct ib_uobject * +__uobj_alloc(const struct uverbs_api_object *obj, struct ib_uverbs_file *ufile, + struct ib_device **ib_dev) { - struct ib_uobject *uobj = rdma_alloc_begin_uobject(type, ufile); + struct ib_uobject *uobj = rdma_alloc_begin_uobject(obj, ufile); if (!IS_ERR(uobj)) *ib_dev = uobj->context->device; @@ -136,7 +138,7 @@ static inline struct ib_uobject *__uobj_alloc(const struct uverbs_obj_type *type } #define uobj_alloc(_type, _ufile, _ib_dev) \ - __uobj_alloc(uobj_get_type(_type), _ufile, _ib_dev) + __uobj_alloc(uobj_get_type(_ufile, _type), _ufile, _ib_dev) #endif diff --git a/include/rdma/uverbs_types.h b/include/rdma/uverbs_types.h index 1ab9a85eebd9f2..acb1bfa3cc99a5 100644 --- a/include/rdma/uverbs_types.h +++ b/include/rdma/uverbs_types.h @@ -37,6 +37,7 @@ #include <rdma/ib_verbs.h> struct uverbs_obj_type; +struct uverbs_api_object; enum rdma_lookup_mode { UVERBS_LOOKUP_READ, @@ -81,14 +82,14 @@ enum rdma_lookup_mode { * alloc_abort returns. */ struct uverbs_obj_type_class { - struct ib_uobject *(*alloc_begin)(const struct uverbs_obj_type *type, + struct ib_uobject *(*alloc_begin)(const struct uverbs_api_object *obj, struct ib_uverbs_file *ufile); /* This consumes the kref on uobj */ int (*alloc_commit)(struct ib_uobject *uobj); /* This does not consume the kref on uobj */ void (*alloc_abort)(struct ib_uobject *uobj); - struct ib_uobject *(*lookup_get)(const struct uverbs_obj_type *type, + struct ib_uobject *(*lookup_get)(const struct uverbs_api_object *obj, struct ib_uverbs_file *ufile, s64 id, enum rdma_lookup_mode mode); void (*lookup_put)(struct ib_uobject *uobj, enum rdma_lookup_mode mode); @@ -128,12 +129,12 @@ struct uverbs_obj_idr_type { enum rdma_remove_reason why); }; -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); void rdma_lookup_put_uobject(struct ib_uobject *uobj, enum rdma_lookup_mode mode); -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); void rdma_alloc_abort_uobject(struct ib_uobject *uobj); int __must_check rdma_alloc_commit_uobject(struct ib_uobject *uobj); -- 2.18.0