diff options
Diffstat (limited to 'drivers/infiniband/core/uverbs_ioctl.c')
-rw-r--r-- | drivers/infiniband/core/uverbs_ioctl.c | 103 |
1 files changed, 88 insertions, 15 deletions
diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c index 8d32c4ae368c..20be6835291e 100644 --- a/drivers/infiniband/core/uverbs_ioctl.c +++ b/drivers/infiniband/core/uverbs_ioctl.c @@ -114,9 +114,27 @@ static int uverbs_process_attr(struct ib_device *ibdev, uattr->attr_data.reserved) return -EINVAL; - e->ptr_attr.data = uattr->data; e->ptr_attr.len = uattr->len; e->ptr_attr.flags = uattr->flags; + + if (val_spec->flags & UVERBS_ATTR_SPEC_F_ALLOC_AND_COPY && + !uverbs_attr_ptr_is_inline(e)) { + void *p; + + p = kvmalloc(uattr->len, GFP_KERNEL); + if (!p) + return -ENOMEM; + + e->ptr_attr.ptr = p; + + if (copy_from_user(p, u64_to_user_ptr(uattr->data), + uattr->len)) { + kvfree(p); + return -EFAULT; + } + } else { + e->ptr_attr.data = uattr->data; + } break; case UVERBS_ATTR_TYPE_IDR: @@ -167,6 +185,53 @@ static int uverbs_process_attr(struct ib_device *ibdev, return 0; } +static int uverbs_finalize_attrs(struct uverbs_attr_bundle *attrs_bundle, + struct uverbs_attr_spec_hash *const *spec_hash, + size_t num, bool commit) +{ + unsigned int i; + int ret = 0; + + for (i = 0; i < num; i++) { + struct uverbs_attr_bundle_hash *curr_bundle = + &attrs_bundle->hash[i]; + const struct uverbs_attr_spec_hash *curr_spec_bucket = + spec_hash[i]; + unsigned int j; + + if (!curr_spec_bucket) + continue; + + for (j = 0; j < curr_bundle->num_attrs; j++) { + struct uverbs_attr *attr; + const struct uverbs_attr_spec *spec; + + if (!uverbs_attr_is_valid_in_hash(curr_bundle, j)) + continue; + + attr = &curr_bundle->attrs[j]; + spec = &curr_spec_bucket->attrs[j]; + + if (spec->type == UVERBS_ATTR_TYPE_IDR || + spec->type == UVERBS_ATTR_TYPE_FD) { + int current_ret; + + current_ret = uverbs_finalize_object( + attr->obj_attr.uobject, + spec->obj.access, commit); + if (!ret) + ret = current_ret; + } else if (spec->type == UVERBS_ATTR_TYPE_PTR_IN && + spec->flags & + UVERBS_ATTR_SPEC_F_ALLOC_AND_COPY && + !uverbs_attr_ptr_is_inline(attr)) { + kvfree(attr->ptr_attr.ptr); + } + } + } + return ret; +} + static int uverbs_uattrs_process(struct ib_device *ibdev, struct ib_ucontext *ucontext, const struct ib_uverbs_attr *uattrs, @@ -185,12 +250,12 @@ static int uverbs_uattrs_process(struct ib_device *ibdev, struct uverbs_attr_spec_hash *attr_spec_bucket; ret = uverbs_ns_idx(&attr_id, method->num_buckets); - if (ret < 0) { + if (ret < 0 || !method->attr_buckets[ret]) { if (uattr->flags & UVERBS_ATTR_F_MANDATORY) { - uverbs_finalize_objects(attr_bundle, - method->attr_buckets, - num_given_buckets, - false); + uverbs_finalize_attrs(attr_bundle, + method->attr_buckets, + num_given_buckets, + false); return ret; } continue; @@ -208,10 +273,10 @@ static int uverbs_uattrs_process(struct ib_device *ibdev, attr_spec_bucket, &attr_bundle->hash[ret], uattr_ptr++); if (ret) { - uverbs_finalize_objects(attr_bundle, - method->attr_buckets, - num_given_buckets, - false); + uverbs_finalize_attrs(attr_bundle, + method->attr_buckets, + num_given_buckets, + false); return ret; } } @@ -228,6 +293,9 @@ static int uverbs_validate_kernel_mandatory(const struct uverbs_method_spec *met struct uverbs_attr_spec_hash *attr_spec_bucket = method_spec->attr_buckets[i]; + if (!attr_spec_bucket) + continue; + if (!bitmap_subset(attr_spec_bucket->mandatory_attrs_bitmask, attr_bundle->hash[i].valid_bitmap, attr_spec_bucket->num_attrs)) @@ -271,10 +339,10 @@ static int uverbs_handle_method(struct ib_uverbs_attr __user *uattr_ptr, ret = method_spec->handler(ibdev, ufile, attr_bundle); cleanup: - finalize_ret = uverbs_finalize_objects(attr_bundle, - method_spec->attr_buckets, - attr_bundle->num_buckets, - !ret); + finalize_ret = uverbs_finalize_attrs(attr_bundle, + method_spec->attr_buckets, + attr_bundle->num_buckets, + !ret); return ret ? ret : finalize_ret; } @@ -341,7 +409,12 @@ static long ib_uverbs_cmd_verbs(struct ib_device *ib_dev, * filled at a later stage (uverbs_process_attr) */ for (i = 0; i < method_spec->num_buckets; i++) { - unsigned int curr_num_attrs = method_spec->attr_buckets[i]->num_attrs; + unsigned int curr_num_attrs; + + if (!method_spec->attr_buckets[i]) + continue; + + curr_num_attrs = method_spec->attr_buckets[i]->num_attrs; ctx->uverbs_attr_bundle->hash[i].attrs = curr_attr; curr_attr += curr_num_attrs; |