mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-23 08:37:33 -04:00
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 <jgg@nvidia.com>
This commit is contained in:
@@ -501,3 +501,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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user