/* * 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 /* * Check if the command buffer provided by the driver includes anything that * is not compatible with the legacy interface. If so, then * _execute_ioctl_fallback indicates it handled the call and sets the error * code */ enum write_fallback _check_legacy(struct ibv_command_buffer *cmdb, int *ret) { struct ib_uverbs_attr *cur; bool fallback_require_ex = cmdb->fallback_require_ex; bool fallback_ioctl_only = cmdb->fallback_ioctl_only; for (cmdb = cmdb->next; cmdb; cmdb = cmdb->next) { for (cur = cmdb->hdr.attrs; cur != cmdb->next_attr; cur++) { if (cur->attr_id != UVERBS_ATTR_UHW_IN && cur->attr_id != UVERBS_ATTR_UHW_OUT && cur->flags & UVERBS_ATTR_F_MANDATORY) goto not_supp; } fallback_require_ex |= cmdb->fallback_require_ex; fallback_ioctl_only |= cmdb->fallback_ioctl_only; } if (fallback_ioctl_only) goto not_supp; if (fallback_require_ex) return TRY_WRITE_EX; return TRY_WRITE; not_supp: errno = EOPNOTSUPP; *ret = EOPNOTSUPP; return ERROR; } /* * Used to support callers that have a fallback to the old write ABI * interface. */ enum write_fallback _execute_ioctl_fallback(struct ibv_context *ctx, unsigned int cmd_bit, struct ibv_command_buffer *cmdb, int *ret) { struct verbs_ex_private *priv = get_priv(ctx); if (bitmap_test_bit(priv->unsupported_ioctls, cmd_bit)) return _check_legacy(cmdb, ret); *ret = execute_ioctl(ctx, cmdb); if (likely(*ret == 0)) return SUCCESS; if (*ret == ENOTTY) { /* ENOTTY means the ioctl framework is entirely absent */ bitmap_fill(priv->unsupported_ioctls, VERBS_OPS_NUM); return _check_legacy(cmdb, ret); } if (*ret == EPROTONOSUPPORT || *ret == 524 || *ret == EINVAL) { /* * EPROTONOSUPPORT means we have the ioctl framework but this * specific method is not supported */ bitmap_set_bit(priv->unsupported_ioctls, cmd_bit); return _check_legacy(cmdb, ret); } return ERROR; } /* * Within the command implementation we get a pointer to the request and * response buffers for the legacy interface. This pointer is either allocated * on the stack (if the driver didn't provide a UHW) or arranged to be * directly before the UHW memory (see _write_set_uhw) */ void *_write_get_req(struct ibv_command_buffer *link, struct ib_uverbs_cmd_hdr *onstack, size_t size) { struct ib_uverbs_cmd_hdr *hdr; size += sizeof(*hdr); if (link->uhw_in_idx != _UHW_NO_INDEX) { struct ib_uverbs_attr *uhw = &link->hdr.attrs[link->uhw_in_idx]; assert(uhw->attr_id == UVERBS_ATTR_UHW_IN); assert(link->uhw_in_headroom_dwords * 4 >= size); hdr = (void *)((uintptr_t)uhw->data - size); hdr->in_words = __check_divide(size + uhw->len, 4); } else { hdr = onstack; hdr->in_words = __check_divide(size, 4); } return hdr + 1; } void *_write_get_req_ex(struct ibv_command_buffer *link, struct ex_hdr *onstack, size_t size) { struct ex_hdr *hdr; size_t full_size = size + sizeof(*hdr); if (link->uhw_in_idx != _UHW_NO_INDEX) { struct ib_uverbs_attr *uhw = &link->hdr.attrs[link->uhw_in_idx]; assert(uhw->attr_id == UVERBS_ATTR_UHW_IN); assert(link->uhw_in_headroom_dwords * 4 >= full_size); hdr = (void *)((uintptr_t)uhw->data - full_size); hdr->ex_hdr.provider_in_words = __check_divide(uhw->len, 8); } else { hdr = onstack; hdr->ex_hdr.provider_in_words = 0; } return hdr + 1; } void *_write_get_resp(struct ibv_command_buffer *link, struct ib_uverbs_cmd_hdr *hdr, void *onstack, size_t resp_size) { void *resp_start; if (link->uhw_out_idx != _UHW_NO_INDEX) { struct ib_uverbs_attr *uhw = &link->hdr.attrs[link->uhw_out_idx]; assert(uhw->attr_id == UVERBS_ATTR_UHW_OUT); assert(link->uhw_out_headroom_dwords * 4 >= resp_size); resp_start = (void *)((uintptr_t)uhw->data - resp_size); hdr->out_words = __check_divide(resp_size + uhw->len, 4); } else { resp_start = onstack; hdr->out_words = __check_divide(resp_size, 4); } return resp_start; } void *_write_get_resp_ex(struct ibv_command_buffer *link, struct ex_hdr *hdr, void *onstack, size_t resp_size) { void *resp_start; if (link->uhw_out_idx != _UHW_NO_INDEX) { struct ib_uverbs_attr *uhw = &link->hdr.attrs[link->uhw_out_idx]; assert(uhw->attr_id == UVERBS_ATTR_UHW_OUT); assert(link->uhw_out_headroom_dwords * 4 >= resp_size); resp_start = (void *)((uintptr_t)uhw->data - resp_size); hdr->ex_hdr.provider_out_words = __check_divide(uhw->len, 8); } else { resp_start = onstack; hdr->ex_hdr.provider_out_words = 0; } return resp_start; } static int ioctl_write(struct ibv_context *ctx, unsigned int write_method, const void *req, size_t core_req_size, size_t req_size, void *resp, size_t core_resp_size, size_t resp_size) { DECLARE_COMMAND_BUFFER(cmdb, UVERBS_OBJECT_DEVICE, UVERBS_METHOD_INVOKE_WRITE, 5); fill_attr_const_in(cmdb, UVERBS_ATTR_WRITE_CMD, write_method); if (core_req_size) fill_attr_in(cmdb, UVERBS_ATTR_CORE_IN, req, core_req_size); if (core_resp_size) fill_attr_out(cmdb, UVERBS_ATTR_CORE_OUT, resp, core_resp_size); if (req_size - core_req_size) fill_attr_in(cmdb, UVERBS_ATTR_UHW_IN, req + core_req_size, req_size - core_req_size); if (resp_size - core_resp_size) fill_attr_out(cmdb, UVERBS_ATTR_UHW_OUT, resp + core_resp_size, resp_size - core_resp_size); return execute_ioctl(ctx, cmdb); } int _execute_cmd_write(struct ibv_context *ctx, unsigned int write_method, struct ib_uverbs_cmd_hdr *req, size_t core_req_size, size_t req_size, void *resp, size_t core_resp_size, size_t resp_size) { struct verbs_ex_private *priv = get_priv(ctx); if (!VERBS_WRITE_ONLY && (VERBS_IOCTL_ONLY || priv->use_ioctl_write)) return ioctl_write(ctx, write_method, req + 1, core_req_size - sizeof(*req), req_size - sizeof(*req), resp, core_resp_size, resp_size); req->command = write_method; req->in_words = __check_divide(req_size, 4); req->out_words = __check_divide(resp_size, 4); if (write(ctx->cmd_fd, req, req_size) != req_size) return errno; if (resp) VALGRIND_MAKE_MEM_DEFINED(resp, resp_size); return 0; } /* * req_size is the total length of the ex_hdr, core payload and driver data. * core_req_size is the total length of the ex_hdr and core_payload. */ int _execute_cmd_write_ex(struct ibv_context *ctx, unsigned int write_method, struct ex_hdr *req, size_t core_req_size, size_t req_size, void *resp, size_t core_resp_size, size_t resp_size) { struct verbs_ex_private *priv = get_priv(ctx); if (!VERBS_WRITE_ONLY && (VERBS_IOCTL_ONLY || priv->use_ioctl_write)) return ioctl_write( ctx, IB_USER_VERBS_CMD_FLAG_EXTENDED | write_method, req + 1, core_req_size - sizeof(*req), req_size - sizeof(*req), resp, core_resp_size, resp_size); req->hdr.command = IB_USER_VERBS_CMD_FLAG_EXTENDED | write_method; req->hdr.in_words = __check_divide(core_req_size - sizeof(struct ex_hdr), 8); req->hdr.out_words = __check_divide(core_resp_size, 8); req->ex_hdr.provider_in_words = __check_divide(req_size - core_req_size, 8); req->ex_hdr.provider_out_words = __check_divide(resp_size - core_resp_size, 8); req->ex_hdr.response = ioctl_ptr_to_u64(resp); req->ex_hdr.cmd_hdr_reserved = 0; /* * Users assumes the stack buffer is zeroed before passing to the * kernel for writing. New kernels with the ioctl path do this * automatically for us. */ if (resp) memset(resp, 0, resp_size); if (write(ctx->cmd_fd, req, req_size) != req_size) return errno; if (resp) VALGRIND_MAKE_MEM_DEFINED(resp, resp_size); return 0; }