/* * Copyright (c) 2017, Mellanox Technologies inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE.
*/
/* * Must be last. bundle ends in a flex array which overlaps * internal_buffer.
*/ struct uverbs_attr_bundle_hdr bundle;
u64 internal_buffer[32];
};
/* * Each method has an absolute minimum amount of memory it needs to allocate, * precompute that amount and determine if the onstack memory can be used or * if allocation is need.
*/ void uapi_compute_bundle_size(struct uverbs_api_ioctl_method *method_elm, unsignedint num_attrs)
{ struct bundle_priv *pbundle; struct uverbs_attr_bundle *bundle;
size_t bundle_size =
offsetof(struct bundle_priv, internal_buffer) + sizeof(*bundle->attrs) * method_elm->key_bitmap_len + sizeof(*pbundle->uattrs) * num_attrs;
/* Do not want order-2 allocations for this. */
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;
attr->uobjects =
uverbs_alloc(bundle,
array_size(array_len, sizeof(*attr->uobjects))); if (IS_ERR(attr->uobjects)) return PTR_ERR(attr->uobjects);
/* * Since idr is 4B and *uobjects is >= 4B, we can use attr->uobjects * to store idrs array and avoid additional memory allocation. The * idrs array is offset to the end of the uobjects array so we will be * able to read idr and replace with a pointer.
*/
idr_vals = (u32 *)(attr->uobjects + array_len) - array_len;
if (uattr->len > sizeof(uattr->data)) {
ret = copy_from_user(idr_vals, u64_to_user_ptr(uattr->data),
uattr->len); if (ret) return -EFAULT;
} else {
memcpy(idr_vals, &uattr->data, uattr->len);
}
for (i = 0; i != array_len; i++) {
attr->uobjects[i] = uverbs_get_uobject_from_file(
spec->u2.objs_arr.obj_type, spec->u2.objs_arr.access,
idr_vals[i], bundle); if (IS_ERR(attr->uobjects[i])) {
ret = PTR_ERR(attr->uobjects[i]); break;
}
}
/* Currently we only support PTR_IN based enums */ if (val_spec->type != UVERBS_ATTR_TYPE_PTR_IN) return -EOPNOTSUPP;
e->ptr_attr.enum_id = uattr->attr_data.enum_data.elem_id;
fallthrough; case UVERBS_ATTR_TYPE_PTR_IN: /* Ensure that any data provided by userspace beyond the known * struct is zero. Userspace that knows how to use some future * longer struct will fail here if used with an old kernel and * non-zero content, making ABI compat/discovery simpler.
*/ if (uattr->len > val_spec->u.ptr.len &&
val_spec->zero_trailing &&
!uverbs_is_attr_cleared(uattr, val_spec->u.ptr.len)) return -EOPNOTSUPP;
fallthrough; case UVERBS_ATTR_TYPE_PTR_OUT: if (uattr->len < val_spec->u.ptr.min_len ||
(!val_spec->zero_trailing &&
uattr->len > val_spec->u.ptr.len)) return -EINVAL;
if (spec->type != UVERBS_ATTR_TYPE_ENUM_IN &&
uattr->attr_data.reserved) return -EINVAL;
/* * The type of uattr->data is u64 for UVERBS_ATTR_TYPE_IDR and * s64 for UVERBS_ATTR_TYPE_FD. We can cast the u64 to s64 * here without caring about truncation as we know that the * IDR implementation today rejects negative IDs
*/
o_attr->uobject = uverbs_get_uobject_from_file(
spec->u.obj.obj_type, spec->u.obj.access,
uattr->data_s64, bundle); if (IS_ERR(o_attr->uobject)) return PTR_ERR(o_attr->uobject);
__set_bit(attr_bkey, pbundle->uobj_finalize);
if (spec->u.obj.access == UVERBS_ACCESS_NEW) { unsignedint uattr_idx = uattr - pbundle->uattrs;
s64 id = o_attr->uobject->id;
/* Copy the allocated id to the user-space */ if (put_user(id, &pbundle->user_attrs[uattr_idx].data)) return -EFAULT;
}
break;
case UVERBS_ATTR_TYPE_RAW_FD: if (uattr->attr_data.reserved || uattr->len != 0 ||
uattr->data_s64 < INT_MIN || uattr->data_s64 > INT_MAX) return -EINVAL; /* _uverbs_get_const_signed() is the accessor */
e->ptr_attr.data = uattr->data_s64; break;
/* * We search the radix tree with the method prefix and now we want to fast * search the suffix bits to get a particular attribute pointer. It is not * totally clear to me if this breaks the radix tree encasulation or not, but * it uses the iter data to determine if the method iter points at the same * chunk that will store the attribute, if so it just derefs it directly. By * construction in most kernel configs the method and attrs will all fit in a * single radix chunk, so in most cases this will have no search. Other cases * this falls back to a full search.
*/ staticvoid __rcu **uapi_get_attr_for_method(struct bundle_priv *pbundle,
u32 attr_key)
{ void __rcu **slot;
if (likely(attr_key < pbundle->radix_slots_len)) { void *entry;
slot = uapi_get_attr_for_method(pbundle, attr_key); if (!slot) { /* * Kernel does not support the attribute but user-space says it * is mandatory
*/ if (uattr->flags & UVERBS_ATTR_F_MANDATORY) return -EPROTONOSUPPORT; return 0;
}
attr = rcu_dereference_protected(*slot, true);
/* Reject duplicate attributes from user-space */ if (test_bit(attr_bkey, pbundle->bundle.attr_present)) return -EINVAL;
ret = uverbs_process_attr(pbundle, attr, uattr, attr_bkey); if (ret) return ret;
/* See uverbs_disassociate_api() */
handler = srcu_dereference(
pbundle->method_elm->handler,
&pbundle->bundle.ufile->device->disassociate_srcu); if (!handler) return -EIO;
pbundle->uattrs = uverbs_alloc(bundle, uattrs_size); if (IS_ERR(pbundle->uattrs)) return PTR_ERR(pbundle->uattrs); if (copy_from_user(pbundle->uattrs, pbundle->user_attrs, uattrs_size)) return -EFAULT;
for (i = 0; i != num_attrs; i++) {
ret = uverbs_set_attr(pbundle, &pbundle->uattrs[i]); if (unlikely(ret)) return ret;
}
/* User space did not provide all the mandatory attributes */ if (unlikely(!bitmap_subset(pbundle->method_elm->attr_mandatory,
pbundle->bundle.attr_present,
pbundle->method_elm->key_bitmap_len))) return -EINVAL;
if (destroy_bkey != UVERBS_API_ATTR_BKEY_LEN) { struct uverbs_obj_attr *destroy_attr = &bundle->attrs[destroy_bkey].obj_attr;
ret = uobj_destroy(destroy_attr->uobject, bundle); if (ret) return ret;
__clear_bit(destroy_bkey, pbundle->uobj_finalize);
ret = handler(bundle);
uobj_put_destroy(destroy_attr->uobject);
} else {
ret = handler(bundle);
}
/* * Until the drivers are revised to use the bundle directly we have to * assume that the driver wrote to its UHW_OUT and flag userspace * appropriately.
*/ if (!ret && pbundle->method_elm->has_udata) { conststruct uverbs_attr *attr =
uverbs_attr_get(bundle, UVERBS_ATTR_UHW_OUT);
if (!IS_ERR(attr))
ret = uverbs_set_output(bundle, attr);
}
/* * EPROTONOSUPPORT is ONLY to be returned if the ioctl framework can * not invoke the method because the request is not supported. No * other cases should return this code.
*/ if (WARN_ON_ONCE(ret == -EPROTONOSUPPORT)) return -EINVAL;
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; elseif (attr->ptr_attr.len == 4)
flags = *(u32 *)&attr->ptr_attr.data; else return -EINVAL;
int uverbs_get_flags32(u32 *to, conststruct 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 * ib_udata format used by the drivers.
*/ void uverbs_fill_udata(struct uverbs_attr_bundle *bundle, struct ib_udata *udata, unsignedint attr_in, unsignedint attr_out)
{ struct bundle_priv *pbundle =
container_of(&bundle->hdr, struct bundle_priv, bundle); struct uverbs_attr_bundle *bundle_aux =
container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr); conststruct uverbs_attr *in =
uverbs_attr_get(bundle_aux, attr_in); conststruct uverbs_attr *out =
uverbs_attr_get(bundle_aux, attr_out);
/* * 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.
*/ int uverbs_output_written(conststruct uverbs_attr_bundle *bundle, size_t idx)
{ conststruct uverbs_attr *attr = uverbs_attr_get(bundle, idx);
/* Once called an abort will call through to the type's destroy_hw() */ void uverbs_finalize_uobj_create(conststruct uverbs_attr_bundle *bundle,
u16 idx)
{ struct bundle_priv *pbundle =
container_of(&bundle->hdr, struct bundle_priv, bundle);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.