| /* | |
| * Copyright (c) 2018 Mellanox Technologies, Ltd. 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. | |
| */ | |
| #ifndef __INFINIBAND_VERBS_IOCTL_H | |
| #define __INFINIBAND_VERBS_IOCTL_H | |
| #include <config.h> | |
| #include <stdint.h> | |
| #include <assert.h> | |
| #include <rdma/rdma_user_ioctl_cmds.h> | |
| #include <infiniband/verbs.h> | |
| #include <ccan/container_of.h> | |
| #include <util/compiler.h> | |
| static inline uint64_t ioctl_ptr_to_u64(const void *ptr) | |
| { | |
| if (sizeof(ptr) == sizeof(uint64_t)) | |
| return (uintptr_t)ptr; | |
| /* | |
| * Some CPU architectures require sign extension when converting from | |
| * a 32 bit to 64 bit pointer. This should match the kernel | |
| * implementation of compat_ptr() for the architecture. | |
| */ | |
| #if defined(__tilegx__) | |
| return (int64_t)(intptr_t)ptr; | |
| #else | |
| return (uintptr_t)ptr; | |
| #endif | |
| } | |
| static inline void _scrub_ptr_attr(void **ptr) | |
| { | |
| #if UINTPTR_MAX == UINT64_MAX | |
| /* Do nothing */ | |
| #else | |
| RDMA_UAPI_PTR(void *, data) *scrub_data; | |
| scrub_data = container_of(ptr, typeof(*scrub_data), data); | |
| scrub_data->data_data_u64 = ioctl_ptr_to_u64(scrub_data->data); | |
| #endif | |
| } | |
| #define scrub_ptr_attr(ptr) _scrub_ptr_attr((void **)(&ptr)) | |
| /* | |
| * The command buffer is organized as a linked list of blocks of attributes. | |
| * Each stack frame allocates its block and then calls up toward to core code | |
| * which will do the ioctl. The frame that does the ioctl calls the special | |
| * FINAL variant which will allocate enough space to linearize the attribute | |
| * buffer for the kernel. | |
| * | |
| * The current range of attributes to fill is next_attr -> last_attr. | |
| */ | |
| struct ibv_command_buffer { | |
| struct ibv_command_buffer *next; | |
| struct ib_uverbs_attr *next_attr; | |
| struct ib_uverbs_attr *last_attr; | |
| /* | |
| * Used by the legacy write interface to keep track of where the UHW | |
| * buffer is located and the 'headroom' space that the common code | |
| * uses to construct the command header and common command struct | |
| * directly before the drivers' UHW. | |
| */ | |
| uint8_t uhw_in_idx; | |
| uint8_t uhw_out_idx; | |
| uint8_t uhw_in_headroom_dwords; | |
| uint8_t uhw_out_headroom_dwords; | |
| uint8_t buffer_error:1; | |
| /* | |
| * These flags control what execute_ioctl_fallback does if the kernel | |
| * does not support ioctl | |
| */ | |
| uint8_t fallback_require_ex:1; | |
| uint8_t fallback_ioctl_only:1; | |
| struct ib_uverbs_ioctl_hdr hdr; | |
| }; | |
| enum {_UHW_NO_INDEX = 0xFF}; | |
| /* | |
| * Constructing an array of ibv_command_buffer is a reasonable way to expand | |
| * the VLA in hdr.attrs on the stack and also allocate some internal state in | |
| * a single contiguous stack memory region. It will over-allocate the region in | |
| * some cases, but this approach allows the number of elements to be dynamic, | |
| * and not fixed as a compile time constant. | |
| */ | |
| #define _IOCTL_NUM_CMDB(_num_attrs) \ | |
| ((sizeof(struct ibv_command_buffer) + \ | |
| sizeof(struct ib_uverbs_attr) * (_num_attrs) + \ | |
| sizeof(struct ibv_command_buffer) - 1) / \ | |
| sizeof(struct ibv_command_buffer)) | |
| unsigned int __ioctl_final_num_attrs(unsigned int num_attrs, | |
| struct ibv_command_buffer *link); | |
| /* If the user doesn't provide a link then don't create a VLA */ | |
| #define _ioctl_final_num_attrs(_num_attrs, _link) \ | |
| ((__builtin_constant_p(!(_link)) && !(_link)) \ | |
| ? (_num_attrs) \ | |
| : __ioctl_final_num_attrs(_num_attrs, _link)) | |
| #define _COMMAND_BUFFER_INIT(_hdr, _object_id, _method_id, _num_attrs, _link) \ | |
| ((struct ibv_command_buffer){ \ | |
| .hdr = \ | |
| { \ | |
| .object_id = (_object_id), \ | |
| .method_id = (_method_id), \ | |
| }, \ | |
| .next = _link, \ | |
| .uhw_in_idx = _UHW_NO_INDEX, \ | |
| .uhw_out_idx = _UHW_NO_INDEX, \ | |
| .next_attr = (_hdr).attrs, \ | |
| .last_attr = (_hdr).attrs + _num_attrs}) | |
| /* | |
| * C99 does not permit an initializer for VLAs, so this function does the init | |
| * instead. It is called in the wonky way so that DELCARE_COMMAND_BUFFER can | |
| * still be a 'variable', and we so we don't require C11 mode. | |
| */ | |
| static inline int _ioctl_init_cmdb(struct ibv_command_buffer *cmd, | |
| uint16_t object_id, uint16_t method_id, | |
| size_t num_attrs, | |
| struct ibv_command_buffer *link) | |
| { | |
| *cmd = _COMMAND_BUFFER_INIT(cmd->hdr, object_id, method_id, num_attrs, | |
| link); | |
| return 0; | |
| } | |
| /* | |
| * Construct an IOCTL command buffer on the stack with enough space for | |
| * _num_attrs elements. _num_attrs does not have to be a compile time constant. | |
| * _link is a previous COMMAND_BUFFER in the call chain. | |
| */ | |
| #ifndef __CHECKER__ | |
| #define DECLARE_COMMAND_BUFFER_LINK(_name, _object_id, _method_id, _num_attrs, \ | |
| _link) \ | |
| const unsigned int __##_name##total = \ | |
| _ioctl_final_num_attrs(_num_attrs, _link); \ | |
| struct ibv_command_buffer _name[_IOCTL_NUM_CMDB(__##_name##total)]; \ | |
| int __attribute__((unused)) __##_name##dummy = _ioctl_init_cmdb( \ | |
| _name, _object_id, _method_id, __##_name##total, _link) | |
| #else | |
| /* | |
| * sparse enforces kernel rules which forbids VLAs. Make the VLA into a static | |
| * array when running sparse. Don't actually run the sparse compile result. | |
| * Sparse also doesn't like arrays of VLAs | |
| */ | |
| #define DECLARE_COMMAND_BUFFER_LINK(_name, _object_id, _method_id, _num_attrs, \ | |
| _link) \ | |
| uint64_t __##_name##storage[10]; \ | |
| struct ibv_command_buffer *_name = (void *)__##_name##storage[10]; \ | |
| int __attribute__((unused)) __##_name##dummy = \ | |
| _ioctl_init_cmdb(_name, _object_id, _method_id, 10, _link) | |
| #endif | |
| #define DECLARE_COMMAND_BUFFER(_name, _object_id, _method_id, _num_attrs) \ | |
| DECLARE_COMMAND_BUFFER_LINK(_name, _object_id, _method_id, _num_attrs, \ | |
| NULL) | |
| int execute_ioctl(struct ibv_context *context, struct ibv_command_buffer *cmd); | |
| static inline struct ib_uverbs_attr * | |
| _ioctl_next_attr(struct ibv_command_buffer *cmd, uint16_t attr_id) | |
| { | |
| struct ib_uverbs_attr *attr; | |
| assert(cmd->next_attr < cmd->last_attr); | |
| attr = cmd->next_attr++; | |
| *attr = (struct ib_uverbs_attr){ | |
| .attr_id = attr_id, | |
| /* | |
| * All attributes default to mandatory. Wrapper the fill_* | |
| * call in attr_optional() to make it optional. | |
| */ | |
| .flags = UVERBS_ATTR_F_MANDATORY, | |
| }; | |
| return attr; | |
| } | |
| /* | |
| * This construction is insane, an expression with a side effect that returns | |
| * from the calling function, but it is a non-invasive way to get the compiler | |
| * to elide the IOCTL support in the backwards compat command functions | |
| * without disturbing native ioctl support. | |
| * | |
| * A command function will set last_attr on the stack to NULL, and if it is | |
| * coded properly, the compiler will prove that last_attr is never changed and | |
| * elide the function. Unfortunately this penalizes native ioctl uses with the | |
| * extra if overhead. | |
| * | |
| * For this reason, _ioctl_next_attr must never be called outside a fill | |
| * function. | |
| */ | |
| #if VERBS_WRITE_ONLY | |
| #define _ioctl_next_attr(cmd, attr_id) \ | |
| ({ \ | |
| if (!((cmd)->last_attr)) \ | |
| return NULL; \ | |
| _ioctl_next_attr(cmd, attr_id); \ | |
| }) | |
| #endif | |
| /* Make the attribute optional. */ | |
| static inline struct ib_uverbs_attr *attr_optional(struct ib_uverbs_attr *attr) | |
| { | |
| if (!attr) | |
| return attr; | |
| attr->flags &= ~UVERBS_ATTR_F_MANDATORY; | |
| return attr; | |
| } | |
| /* Send attributes of kernel type UVERBS_ATTR_TYPE_IDR */ | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_in_obj(struct ibv_command_buffer *cmd, uint16_t attr_id, uint32_t idr) | |
| { | |
| struct ib_uverbs_attr *attr = _ioctl_next_attr(cmd, attr_id); | |
| /* UVERBS_ATTR_TYPE_IDR uses a 64 bit value for the idr # */ | |
| attr->data = idr; | |
| return attr; | |
| } | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_out_obj(struct ibv_command_buffer *cmd, uint16_t attr_id) | |
| { | |
| return fill_attr_in_obj(cmd, attr_id, 0); | |
| } | |
| static inline uint32_t read_attr_obj(uint16_t attr_id, | |
| struct ib_uverbs_attr *attr) | |
| { | |
| assert(attr->attr_id == attr_id); | |
| return attr->data; | |
| } | |
| /* Send attributes of kernel type UVERBS_ATTR_TYPE_PTR_IN */ | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_in(struct ibv_command_buffer *cmd, uint16_t attr_id, const void *data, | |
| size_t len) | |
| { | |
| struct ib_uverbs_attr *attr = _ioctl_next_attr(cmd, attr_id); | |
| if (unlikely(len > UINT16_MAX)) | |
| cmd->buffer_error = 1; | |
| attr->len = len; | |
| if (len <= sizeof(uint64_t) && data) | |
| memcpy(&attr->data, data, len); | |
| else | |
| attr->data = ioctl_ptr_to_u64(data); | |
| return attr; | |
| } | |
| #define fill_attr_in_ptr(cmd, attr_id, ptr) \ | |
| fill_attr_in(cmd, attr_id, ptr, sizeof(*ptr)) | |
| /* Send attributes of various inline kernel types */ | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_in_uint64(struct ibv_command_buffer *cmd, uint16_t attr_id, | |
| uint64_t data) | |
| { | |
| struct ib_uverbs_attr *attr = _ioctl_next_attr(cmd, attr_id); | |
| attr->len = sizeof(data); | |
| attr->data = data; | |
| return attr; | |
| } | |
| #define fill_attr_const_in(cmd, attr_id, _data) \ | |
| fill_attr_in_uint64(cmd, attr_id, _data) | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_in_uint32(struct ibv_command_buffer *cmd, uint16_t attr_id, | |
| uint32_t data) | |
| { | |
| struct ib_uverbs_attr *attr = _ioctl_next_attr(cmd, attr_id); | |
| attr->len = sizeof(data); | |
| memcpy(&attr->data, &data, sizeof(data)); | |
| return attr; | |
| } | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_in_fd(struct ibv_command_buffer *cmd, uint16_t attr_id, int fd) | |
| { | |
| struct ib_uverbs_attr *attr; | |
| if (fd == -1) | |
| return NULL; | |
| attr = _ioctl_next_attr(cmd, attr_id); | |
| /* UVERBS_ATTR_TYPE_FD uses a 64 bit value for the idr # */ | |
| attr->data = fd; | |
| return attr; | |
| } | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_out_fd(struct ibv_command_buffer *cmd, uint16_t attr_id, int fd) | |
| { | |
| struct ib_uverbs_attr *attr = _ioctl_next_attr(cmd, attr_id); | |
| attr->data = 0; | |
| return attr; | |
| } | |
| static inline int read_attr_fd(uint16_t attr_id, struct ib_uverbs_attr *attr) | |
| { | |
| assert(attr->attr_id == attr_id); | |
| /* The kernel cannot fail to create a FD here, it never returns -1 */ | |
| return attr->data; | |
| } | |
| /* Send attributes of kernel type UVERBS_ATTR_TYPE_PTR_OUT */ | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_out(struct ibv_command_buffer *cmd, uint16_t attr_id, void *data, | |
| size_t len) | |
| { | |
| struct ib_uverbs_attr *attr = _ioctl_next_attr(cmd, attr_id); | |
| if (unlikely(len > UINT16_MAX)) | |
| cmd->buffer_error = 1; | |
| attr->len = len; | |
| attr->data = ioctl_ptr_to_u64(data); | |
| return attr; | |
| } | |
| #define fill_attr_out_ptr(cmd, attr_id, ptr) \ | |
| fill_attr_out(cmd, attr_id, ptr, sizeof(*(ptr))) | |
| /* If size*nelems overflows size_t this returns SIZE_MAX */ | |
| static inline size_t _array_len(size_t size, size_t nelems) | |
| { | |
| if (size != 0 && | |
| SIZE_MAX / size <= nelems) | |
| return SIZE_MAX; | |
| return size * nelems; | |
| } | |
| #define fill_attr_out_ptr_array(cmd, attr_id, ptr, nelems) \ | |
| fill_attr_out(cmd, attr_id, ptr, _array_len(sizeof(*ptr), nelems)) | |
| #define fill_attr_in_ptr_array(cmd, attr_id, ptr, nelems) \ | |
| fill_attr_in(cmd, attr_id, ptr, _array_len(sizeof(*ptr), nelems)) | |
| static inline size_t __check_divide(size_t val, unsigned int div) | |
| { | |
| assert(val % div == 0); | |
| return val / div; | |
| } | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_in_enum(struct ibv_command_buffer *cmd, uint16_t attr_id, | |
| uint8_t elem_id, const void *data, size_t len) | |
| { | |
| struct ib_uverbs_attr *attr; | |
| attr = fill_attr_in(cmd, attr_id, data, len); | |
| attr->attr_data.enum_data.elem_id = elem_id; | |
| return attr; | |
| } | |
| /* Send attributes of kernel type UVERBS_ATTR_TYPE_IDRS_ARRAY */ | |
| static inline struct ib_uverbs_attr * | |
| fill_attr_in_objs_arr(struct ibv_command_buffer *cmd, uint16_t attr_id, | |
| const uint32_t *idrs_arr, size_t nelems) | |
| { | |
| return fill_attr_in(cmd, attr_id, idrs_arr, | |
| _array_len(sizeof(*idrs_arr), nelems)); | |
| } | |
| #endif |