| /* |
| * 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 <infiniband/cmd_ioctl.h> |
| #include <infiniband/cmd_write.h> |
| #include "ibverbs.h" |
| |
| #include <sys/ioctl.h> |
| #include <infiniband/driver.h> |
| |
| #include <rdma/ib_user_ioctl_cmds.h> |
| #include <valgrind/memcheck.h> |
| |
| /* 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); |
| } |
| } |