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