From: Jason Gunthorpe Date: Tue, 26 May 2026 01:22:38 +0000 (-0300) Subject: RDMA/core: Move many of the little EXPORTs from uverbs_ioctl into ib_core_uverbs X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=ab2d2b2872aed03cbf0b7c85eeb45fda34d8059b;p=thirdparty%2Flinux.git RDMA/core: Move many of the little EXPORTs from uverbs_ioctl into ib_core_uverbs Not as many drivers need these functions but it does free efa from the ib_uverbs.ko dependency and follows the general design better. Link: https://patch.msgid.link/r/2-v3-43aba1969751+1988-ib_uverbs_support_ko_jgg@nvidia.com Signed-off-by: Jason Gunthorpe --- diff --git a/drivers/infiniband/core/ib_core_uverbs.c b/drivers/infiniband/core/ib_core_uverbs.c index 0acb0d4967cb6..b4fc693a3bd8b 100644 --- a/drivers/infiniband/core/ib_core_uverbs.c +++ b/drivers/infiniband/core/ib_core_uverbs.c @@ -501,3 +501,221 @@ err_fault: return -EFAULT; } EXPORT_SYMBOL(_ib_respond_udata); + +/* + * Must be called with the ufile->device->disassociate_srcu held, and the lock + * must be held until use of the ucontext is finished. + */ +struct ib_ucontext *ib_uverbs_get_ucontext_file(struct ib_uverbs_file *ufile) +{ + /* + * We do not hold the hw_destroy_rwsem lock for this flow, instead + * srcu is used. It does not matter if someone races this with + * get_context, we get NULL or valid ucontext. + */ + struct ib_ucontext *ucontext = smp_load_acquire(&ufile->ucontext); + + if (!srcu_dereference(ufile->device->ib_dev, + &ufile->device->disassociate_srcu)) + return ERR_PTR(-EIO); + + if (!ucontext) + return ERR_PTR(-EINVAL); + + return ucontext; +} +EXPORT_SYMBOL(ib_uverbs_get_ucontext_file); + +int uverbs_destroy_def_handler(struct uverbs_attr_bundle *attrs) +{ + return 0; +} +EXPORT_SYMBOL(uverbs_destroy_def_handler); + +/** + * _uverbs_alloc() - Quickly allocate memory for use with a bundle + * @bundle: The bundle + * @size: Number of bytes to allocate + * @flags: Allocator flags + * + * The bundle allocator is intended for allocations that are connected with + * processing the system call related to the bundle. The allocated memory is + * always freed once the system call completes, and cannot be freed any other + * way. + * + * This tries to use a small pool of pre-allocated memory for performance. + */ +__malloc void *_uverbs_alloc(struct uverbs_attr_bundle *bundle, size_t size, + gfp_t flags) +{ + struct bundle_priv *pbundle = + container_of(&bundle->hdr, struct bundle_priv, bundle); + size_t new_used; + void *res; + + if (check_add_overflow(size, pbundle->internal_used, &new_used)) + return ERR_PTR(-EOVERFLOW); + + if (new_used > pbundle->internal_avail) { + struct bundle_alloc_head *buf; + + buf = kvmalloc_flex(*buf, data, size, flags); + if (!buf) + return ERR_PTR(-ENOMEM); + buf->next = pbundle->allocated_mem; + pbundle->allocated_mem = buf; + return buf->data; + } + + res = (void *)pbundle->internal_buffer + pbundle->internal_used; + pbundle->internal_used = + ALIGN(new_used, sizeof(*pbundle->internal_buffer)); + if (want_init_on_alloc(flags)) + memset(res, 0, size); + return res; +} +EXPORT_SYMBOL(_uverbs_alloc); + +int uverbs_copy_to(const struct uverbs_attr_bundle *bundle, size_t idx, + const void *from, size_t size) +{ + const struct uverbs_attr *attr = uverbs_attr_get(bundle, idx); + size_t min_size; + + if (IS_ERR(attr)) + return PTR_ERR(attr); + + min_size = min_t(size_t, attr->ptr_attr.len, size); + if (copy_to_user(u64_to_user_ptr(attr->ptr_attr.data), from, min_size)) + return -EFAULT; + + return uverbs_set_output(bundle, attr); +} +EXPORT_SYMBOL(uverbs_copy_to); + +int uverbs_copy_to_struct_or_zero(const struct uverbs_attr_bundle *bundle, + size_t idx, const void *from, size_t size) +{ + const struct uverbs_attr *attr = uverbs_attr_get(bundle, idx); + + if (IS_ERR(attr)) + return PTR_ERR(attr); + + if (size < attr->ptr_attr.len) { + if (clear_user(u64_to_user_ptr(attr->ptr_attr.data) + size, + attr->ptr_attr.len - size)) + return -EFAULT; + } + return uverbs_copy_to(bundle, idx, from, size); +} +EXPORT_SYMBOL(uverbs_copy_to_struct_or_zero); + +int _uverbs_get_const_unsigned(u64 *to, + const struct uverbs_attr_bundle *attrs_bundle, + size_t idx, u64 upper_bound, u64 *def_val) +{ + const struct uverbs_attr *attr; + + attr = uverbs_attr_get(attrs_bundle, idx); + if (IS_ERR(attr)) { + if ((PTR_ERR(attr) != -ENOENT) || !def_val) + return PTR_ERR(attr); + + *to = *def_val; + } else { + *to = attr->ptr_attr.data; + } + + if (*to > upper_bound) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(_uverbs_get_const_unsigned); + +int _uverbs_get_const_signed(s64 *to, + const struct uverbs_attr_bundle *attrs_bundle, + size_t idx, s64 lower_bound, u64 upper_bound, + s64 *def_val) +{ + const struct uverbs_attr *attr; + + attr = uverbs_attr_get(attrs_bundle, idx); + if (IS_ERR(attr)) { + if ((PTR_ERR(attr) != -ENOENT) || !def_val) + return PTR_ERR(attr); + + *to = *def_val; + } else { + *to = attr->ptr_attr.data; + } + + if (*to < lower_bound || (*to > 0 && (u64)*to > upper_bound)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(_uverbs_get_const_signed); + +int uverbs_get_flags64(u64 *to, const struct uverbs_attr_bundle *attrs_bundle, + size_t idx, u64 allowed_bits) +{ + const struct uverbs_attr *attr; + u64 flags; + + attr = uverbs_attr_get(attrs_bundle, idx); + /* Missing attribute means 0 flags */ + if (IS_ERR(attr)) { + *to = 0; + return 0; + } + + /* + * New userspace code should use 8 bytes to pass flags, but we + * transparently support old userspaces that were using 4 bytes as + * well. + */ + if (attr->ptr_attr.len == 8) + flags = attr->ptr_attr.data; + else if (attr->ptr_attr.len == 4) + flags = *(u32 *)&attr->ptr_attr.data; + else + return -EINVAL; + + if (flags & ~allowed_bits) + return -EINVAL; + + *to = flags; + return 0; +} +EXPORT_SYMBOL(uverbs_get_flags64); + +int uverbs_get_flags32(u32 *to, const struct uverbs_attr_bundle *attrs_bundle, + size_t idx, u64 allowed_bits) +{ + u64 flags; + int ret; + + ret = uverbs_get_flags64(&flags, attrs_bundle, idx, allowed_bits); + if (ret) + return ret; + + if (flags > U32_MAX) + return -EINVAL; + *to = flags; + + return 0; +} +EXPORT_SYMBOL(uverbs_get_flags32); + +/* Once called an abort will call through to the type's destroy_hw() */ +void uverbs_finalize_uobj_create(const struct uverbs_attr_bundle *bundle, + u16 idx) +{ + struct bundle_priv *pbundle = + container_of(&bundle->hdr, struct bundle_priv, bundle); + + __set_bit(uapi_bkey_attr(uapi_key_attr(idx)), + pbundle->uobj_hw_obj_valid); +} +EXPORT_SYMBOL(uverbs_finalize_uobj_create); diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index f2e192b51e609..1563169c65009 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h @@ -263,6 +263,21 @@ struct bundle_priv { u64 internal_buffer[32]; }; +static inline int uverbs_set_output(const struct uverbs_attr_bundle *bundle, + const struct uverbs_attr *attr) +{ + struct bundle_priv *pbundle = + container_of(&bundle->hdr, struct bundle_priv, bundle); + u16 flags; + + flags = pbundle->uattrs[attr->ptr_attr.uattr_idx].flags | + UVERBS_ATTR_F_VALID_OUTPUT; + if (put_user(flags, + &pbundle->user_attrs[attr->ptr_attr.uattr_idx].flags)) + return -EFAULT; + return 0; +} + long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); struct ib_uverbs_flow_spec { diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c index 2552a7efe2fbe..6a78288e27a1c 100644 --- a/drivers/infiniband/core/uverbs_ioctl.c +++ b/drivers/infiniband/core/uverbs_ioctl.c @@ -58,50 +58,6 @@ void uapi_compute_bundle_size(struct uverbs_api_ioctl_method *method_elm, WARN_ON_ONCE(method_elm->bundle_size > PAGE_SIZE); } -/** - * _uverbs_alloc() - Quickly allocate memory for use with a bundle - * @bundle: The bundle - * @size: Number of bytes to allocate - * @flags: Allocator flags - * - * The bundle allocator is intended for allocations that are connected with - * processing the system call related to the bundle. The allocated memory is - * always freed once the system call completes, and cannot be freed any other - * way. - * - * This tries to use a small pool of pre-allocated memory for performance. - */ -__malloc void *_uverbs_alloc(struct uverbs_attr_bundle *bundle, size_t size, - gfp_t flags) -{ - struct bundle_priv *pbundle = - container_of(&bundle->hdr, struct bundle_priv, bundle); - size_t new_used; - void *res; - - if (check_add_overflow(size, pbundle->internal_used, &new_used)) - return ERR_PTR(-EOVERFLOW); - - if (new_used > pbundle->internal_avail) { - struct bundle_alloc_head *buf; - - buf = kvmalloc_flex(*buf, data, size, flags); - if (!buf) - return ERR_PTR(-ENOMEM); - buf->next = pbundle->allocated_mem; - pbundle->allocated_mem = buf; - return buf->data; - } - - res = (void *)pbundle->internal_buffer + pbundle->internal_used; - pbundle->internal_used = - ALIGN(new_used, sizeof(*pbundle->internal_buffer)); - if (want_init_on_alloc(flags)) - memset(res, 0, size); - return res; -} -EXPORT_SYMBOL(_uverbs_alloc); - static bool uverbs_is_attr_cleared(const struct ib_uverbs_attr *uattr, u16 len) { @@ -113,21 +69,6 @@ static bool uverbs_is_attr_cleared(const struct ib_uverbs_attr *uattr, 0, uattr->len - len); } -static int uverbs_set_output(const struct uverbs_attr_bundle *bundle, - const struct uverbs_attr *attr) -{ - struct bundle_priv *pbundle = - container_of(&bundle->hdr, struct bundle_priv, bundle); - u16 flags; - - flags = pbundle->uattrs[attr->ptr_attr.uattr_idx].flags | - UVERBS_ATTR_F_VALID_OUTPUT; - if (put_user(flags, - &pbundle->user_attrs[attr->ptr_attr.uattr_idx].flags)) - return -EFAULT; - return 0; -} - static int uverbs_process_idrs_array(struct bundle_priv *pbundle, const struct uverbs_api_attr *attr_uapi, struct uverbs_objs_arr_attr *attr, @@ -616,57 +557,6 @@ long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return err; } -int uverbs_get_flags64(u64 *to, const struct uverbs_attr_bundle *attrs_bundle, - size_t idx, u64 allowed_bits) -{ - const struct uverbs_attr *attr; - u64 flags; - - attr = uverbs_attr_get(attrs_bundle, idx); - /* Missing attribute means 0 flags */ - if (IS_ERR(attr)) { - *to = 0; - return 0; - } - - /* - * New userspace code should use 8 bytes to pass flags, but we - * transparently support old userspaces that were using 4 bytes as - * well. - */ - if (attr->ptr_attr.len == 8) - flags = attr->ptr_attr.data; - else if (attr->ptr_attr.len == 4) - flags = *(u32 *)&attr->ptr_attr.data; - else - return -EINVAL; - - if (flags & ~allowed_bits) - return -EINVAL; - - *to = flags; - return 0; -} -EXPORT_SYMBOL(uverbs_get_flags64); - -int uverbs_get_flags32(u32 *to, const struct uverbs_attr_bundle *attrs_bundle, - size_t idx, u64 allowed_bits) -{ - u64 flags; - int ret; - - ret = uverbs_get_flags64(&flags, attrs_bundle, idx, allowed_bits); - if (ret) - return ret; - - if (flags > U32_MAX) - return -EINVAL; - *to = flags; - - return 0; -} -EXPORT_SYMBOL(uverbs_get_flags32); - /* * Fill a ib_udata struct (core or uhw) using the given attribute IDs. * This is primarily used to convert the UVERBS_ATTR_UHW() into the @@ -707,24 +597,6 @@ void uverbs_fill_udata(struct uverbs_attr_bundle *bundle, } } -int uverbs_copy_to(const struct uverbs_attr_bundle *bundle, size_t idx, - const void *from, size_t size) -{ - const struct uverbs_attr *attr = uverbs_attr_get(bundle, idx); - size_t min_size; - - if (IS_ERR(attr)) - return PTR_ERR(attr); - - min_size = min_t(size_t, attr->ptr_attr.len, size); - if (copy_to_user(u64_to_user_ptr(attr->ptr_attr.data), from, min_size)) - return -EFAULT; - - return uverbs_set_output(bundle, attr); -} -EXPORT_SYMBOL(uverbs_copy_to); - - /* * This is only used if the caller has directly used copy_to_use to write the * data. It signals to user space that the buffer is filled in. @@ -738,79 +610,3 @@ int uverbs_output_written(const struct uverbs_attr_bundle *bundle, size_t idx) return uverbs_set_output(bundle, attr); } - -int _uverbs_get_const_signed(s64 *to, - const struct uverbs_attr_bundle *attrs_bundle, - size_t idx, s64 lower_bound, u64 upper_bound, - s64 *def_val) -{ - const struct uverbs_attr *attr; - - attr = uverbs_attr_get(attrs_bundle, idx); - if (IS_ERR(attr)) { - if ((PTR_ERR(attr) != -ENOENT) || !def_val) - return PTR_ERR(attr); - - *to = *def_val; - } else { - *to = attr->ptr_attr.data; - } - - if (*to < lower_bound || (*to > 0 && (u64)*to > upper_bound)) - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL(_uverbs_get_const_signed); - -int _uverbs_get_const_unsigned(u64 *to, - const struct uverbs_attr_bundle *attrs_bundle, - size_t idx, u64 upper_bound, u64 *def_val) -{ - const struct uverbs_attr *attr; - - attr = uverbs_attr_get(attrs_bundle, idx); - if (IS_ERR(attr)) { - if ((PTR_ERR(attr) != -ENOENT) || !def_val) - return PTR_ERR(attr); - - *to = *def_val; - } else { - *to = attr->ptr_attr.data; - } - - if (*to > upper_bound) - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL(_uverbs_get_const_unsigned); - -int uverbs_copy_to_struct_or_zero(const struct uverbs_attr_bundle *bundle, - size_t idx, const void *from, size_t size) -{ - const struct uverbs_attr *attr = uverbs_attr_get(bundle, idx); - - if (IS_ERR(attr)) - return PTR_ERR(attr); - - if (size < attr->ptr_attr.len) { - if (clear_user(u64_to_user_ptr(attr->ptr_attr.data) + size, - attr->ptr_attr.len - size)) - return -EFAULT; - } - return uverbs_copy_to(bundle, idx, from, size); -} -EXPORT_SYMBOL(uverbs_copy_to_struct_or_zero); - -/* Once called an abort will call through to the type's destroy_hw() */ -void uverbs_finalize_uobj_create(const struct uverbs_attr_bundle *bundle, - u16 idx) -{ - struct bundle_priv *pbundle = - container_of(&bundle->hdr, struct bundle_priv, bundle); - - __set_bit(uapi_bkey_attr(uapi_key_attr(idx)), - pbundle->uobj_hw_obj_valid); -} -EXPORT_SYMBOL(uverbs_finalize_uobj_create); diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index f5837da47299c..15d8387718c05 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -91,30 +91,6 @@ static const struct class uverbs_class = { .devnode = uverbs_devnode, }; -/* - * Must be called with the ufile->device->disassociate_srcu held, and the lock - * must be held until use of the ucontext is finished. - */ -struct ib_ucontext *ib_uverbs_get_ucontext_file(struct ib_uverbs_file *ufile) -{ - /* - * We do not hold the hw_destroy_rwsem lock for this flow, instead - * srcu is used. It does not matter if someone races this with - * get_context, we get NULL or valid ucontext. - */ - struct ib_ucontext *ucontext = smp_load_acquire(&ufile->ucontext); - - if (!srcu_dereference(ufile->device->ib_dev, - &ufile->device->disassociate_srcu)) - return ERR_PTR(-EIO); - - if (!ucontext) - return ERR_PTR(-EINVAL); - - return ucontext; -} -EXPORT_SYMBOL(ib_uverbs_get_ucontext_file); - int uverbs_dealloc_mw(struct ib_mw *mw) { struct ib_pd *pd = mw->pd; diff --git a/drivers/infiniband/core/uverbs_std_types.c b/drivers/infiniband/core/uverbs_std_types.c index 13776a66e2e43..e160786e1df16 100644 --- a/drivers/infiniband/core/uverbs_std_types.c +++ b/drivers/infiniband/core/uverbs_std_types.c @@ -165,12 +165,6 @@ uverbs_completion_event_file_destroy_uobj(struct ib_uobject *uobj, ib_uverbs_free_event_queue(&file->ev_queue); } -int uverbs_destroy_def_handler(struct uverbs_attr_bundle *attrs) -{ - return 0; -} -EXPORT_SYMBOL(uverbs_destroy_def_handler); - DECLARE_UVERBS_NAMED_OBJECT( UVERBS_OBJECT_COMP_CHANNEL, UVERBS_TYPE_ALLOC_FD(sizeof(struct ib_uverbs_completion_event_file),