/* * 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 #include #include #include #include #include #include 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