| /* |
| * 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)) |
| 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 |