/* * 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. */ #include #include #include "ibverbs.h" #include #include #include #include /* Number of attrs in this and all the link'd buffers */ unsigned int __ioctl_final_num_attrs(unsigned int num_attrs, struct ibv_command_buffer *link) { for (; link; link = link->next) num_attrs += link->next_attr - link->hdr.attrs; return num_attrs; } /* Linearize the link'd buffers into this one */ static void prepare_attrs(struct ibv_command_buffer *cmd) { struct ib_uverbs_attr *end = cmd->next_attr; struct ibv_command_buffer *link; for (link = cmd->next; link; link = link->next) { struct ib_uverbs_attr *cur; assert(cmd->hdr.object_id == link->hdr.object_id); assert(cmd->hdr.method_id == link->hdr.method_id); /* * Keep track of where the uhw_in lands in the final array if * we copy it from a link */ if (!VERBS_IOCTL_ONLY && link->uhw_in_idx != _UHW_NO_INDEX) { assert(cmd->uhw_in_idx == _UHW_NO_INDEX); cmd->uhw_in_idx = link->uhw_in_idx + (end - cmd->hdr.attrs); } for (cur = link->hdr.attrs; cur != link->next_attr; cur++) *end++ = *cur; assert(end <= cmd->last_attr); } cmd->hdr.num_attrs = end - cmd->hdr.attrs; /* * We keep the in UHW uninlined until directly before sending to * support the compat path. See _fill_attr_in_uhw */ if (!VERBS_IOCTL_ONLY && cmd->uhw_in_idx != _UHW_NO_INDEX) { struct ib_uverbs_attr *uhw = &cmd->hdr.attrs[cmd->uhw_in_idx]; assert(uhw->attr_id == UVERBS_ATTR_UHW_IN); if (uhw->len <= sizeof(uhw->data)) memcpy(&uhw->data, (void *)(uintptr_t)uhw->data, uhw->len); } } static void finalize_attr(struct ib_uverbs_attr *attr) { /* Only matches UVERBS_ATTR_TYPE_PTR_OUT */ if (attr->flags & UVERBS_ATTR_F_VALID_OUTPUT && attr->len) VALGRIND_MAKE_MEM_DEFINED((void *)(uintptr_t)attr->data, attr->len); } /* * Copy the link'd attrs back to their source and make all output buffers safe * for VALGRIND */ static void finalize_attrs(struct ibv_command_buffer *cmd) { struct ibv_command_buffer *link; struct ib_uverbs_attr *end; for (end = cmd->hdr.attrs; end != cmd->next_attr; end++) finalize_attr(end); for (link = cmd->next; link; link = link->next) { struct ib_uverbs_attr *cur; for (cur = link->hdr.attrs; cur != link->next_attr; cur++) { finalize_attr(end); *cur = *end++; } } } int execute_ioctl(struct ibv_context *context, struct ibv_command_buffer *cmd) { struct verbs_context *vctx = verbs_get_ctx(context); /* * One of the fill functions was given input that cannot be marshaled */ if (unlikely(cmd->buffer_error)) { errno = EINVAL; return errno; } prepare_attrs(cmd); cmd->hdr.length = sizeof(cmd->hdr) + sizeof(cmd->hdr.attrs[0]) * cmd->hdr.num_attrs; cmd->hdr.reserved1 = 0; cmd->hdr.reserved2 = 0; cmd->hdr.driver_id = vctx->priv->driver_id; if (ioctl(context->cmd_fd, RDMA_VERBS_IOCTL, &cmd->hdr)) return errno; finalize_attrs(cmd); return 0; } /* * The compat scheme for UHW IN requires a pointer in .data, however the * kernel protocol requires pointers < 8 to be inlined into .data. We defer * that transformation until directly before the ioctl. */ static inline struct ib_uverbs_attr * _fill_attr_in_uhw(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; attr->data = ioctl_ptr_to_u64(data); return attr; } /* * This helper is used in the driver compat wrappers to build the * command buffer from the legacy input pointers format. */ void _write_set_uhw(struct ibv_command_buffer *cmdb, const void *req, size_t core_req_size, size_t req_size, void *resp, size_t core_resp_size, size_t resp_size) { if (req && core_req_size < req_size) { if (VERBS_IOCTL_ONLY) cmdb->uhw_in_idx = fill_attr_in(cmdb, UVERBS_ATTR_UHW_IN, (uint8_t *)req + core_req_size, req_size - core_req_size) - cmdb->hdr.attrs; else cmdb->uhw_in_idx = _fill_attr_in_uhw(cmdb, UVERBS_ATTR_UHW_IN, (uint8_t *)req + core_req_size, req_size - core_req_size) - cmdb->hdr.attrs; cmdb->uhw_in_headroom_dwords = __check_divide(core_req_size, 4); } if (resp && core_resp_size < resp_size) { cmdb->uhw_out_idx = fill_attr_out(cmdb, UVERBS_ATTR_UHW_OUT, (uint8_t *)resp + core_resp_size, resp_size - core_resp_size) - cmdb->hdr.attrs; cmdb->uhw_out_headroom_dwords = __check_divide(core_resp_size, 4); } }