From: Jason Gunthorpe <jgg@nvidia.com>
To: Leon Romanovsky <leon@kernel.org>, linux-rdma@vger.kernel.org
Cc: Jiri Pirko <jiri@resnulli.us>,
patches@lists.linux.dev,
Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
Subject: [PATCH 2/6] RDMA/core: Move many of the little EXPORTs from uverbs_ioctl into ib_core_uverbs
Date: Wed, 13 May 2026 14:33:24 -0300 [thread overview]
Message-ID: <2-v1-045258567bd6+9fe-ib_uverbs_support_ko_jgg@nvidia.com> (raw)
In-Reply-To: <0-v1-045258567bd6+9fe-ib_uverbs_support_ko_jgg@nvidia.com>
Not as many drivers need these functions but it does free efa from the
ib_uverbs.ko dependency and follows the general design better.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/infiniband/core/ib_core_uverbs.c | 218 +++++++++++++++++++++
drivers/infiniband/core/uverbs.h | 15 ++
drivers/infiniband/core/uverbs_ioctl.c | 204 -------------------
drivers/infiniband/core/uverbs_main.c | 24 ---
drivers/infiniband/core/uverbs_std_types.c | 6 -
5 files changed, 233 insertions(+), 234 deletions(-)
diff --git a/drivers/infiniband/core/ib_core_uverbs.c b/drivers/infiniband/core/ib_core_uverbs.c
index c94d03857318eb..11e43a711207e6 100644
--- a/drivers/infiniband/core/ib_core_uverbs.c
+++ b/drivers/infiniband/core/ib_core_uverbs.c
@@ -503,3 +503,221 @@ int _ib_respond_udata(struct ib_udata *udata, const void *src, size_t len)
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 a74a2dff1301ed..e353e195a49c74 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -264,6 +264,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 33feb88d652b6a..0e67de0fff570d 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,
@@ -614,57 +555,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
@@ -705,24 +595,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.
@@ -736,79 +608,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 f5837da47299c1..15d8387718c050 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 13776a66e2e43a..e160786e1df164 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),
--
2.43.0
next prev parent reply other threads:[~2026-05-13 17:33 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-13 17:33 [PATCH 0/6] Remove driver dependencies on ib_uverbs.ko Jason Gunthorpe
2026-05-13 17:33 ` [PATCH 1/6] RDMA/core: Move the _ib_copy_validate_udata* functions to ib_core_uverbs Jason Gunthorpe
2026-05-15 23:12 ` Jason Gunthorpe
2026-05-13 17:33 ` Jason Gunthorpe [this message]
2026-05-13 17:33 ` [PATCH 3/6] RDMA/core: Remove uverbs_async_event_release() Jason Gunthorpe
2026-05-13 17:33 ` [PATCH 4/6] RDMA/core: Make a new module for the uverbs components needed by drivers Jason Gunthorpe
2026-05-13 17:33 ` [PATCH 5/6] RDMA/core: Move ucaps into ib_uverbs_support.ko Jason Gunthorpe
2026-05-13 17:33 ` [PATCH 6/6] RDMA/core: Move flow related functions to ib_uverbs_support.ko Jason Gunthorpe
2026-05-15 18:23 ` kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2-v1-045258567bd6+9fe-ib_uverbs_support_ko_jgg@nvidia.com \
--to=jgg@nvidia.com \
--cc=jiri@resnulli.us \
--cc=leon@kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=patches@lists.linux.dev \
--cc=sriharsha.basavapatna@broadcom.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox