diff mbox

[rdma-core,2/6] verbs: Add basic infrastructure support for the kabi ioctl

Message ID 20180218204002.7408-3-jgg@ziepe.ca (mailing list archive)
State Accepted
Headers show

Commit Message

Jason Gunthorpe Feb. 18, 2018, 8:39 p.m. UTC
From: Jason Gunthorpe <jgg@mellanox.com>

These are macros and helper functions to help build up the ioctl
messages from the calling commands.

The scheme uses a linked list of attribute buffers with the idea that
the driver and core stack frames will allocate these buffers on the
stack.

The ioctl wrapper pulls all of the buffers together into a linear array
and passes that to the kernel, then scatters the answers back to where
they came from.

A collection of 'fill' helpers are provided that mirror the extraction
helpers in the kernel. Each attribute should be used with the
fill helper type that matches what is in the kernel.

Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
---
 libibverbs/CMakeLists.txt    |   2 +
 libibverbs/cmd_ioctl.c       | 112 ++++++++++++++++
 libibverbs/cmd_ioctl.h       | 299 +++++++++++++++++++++++++++++++++++++++++++
 libibverbs/libibverbs.map.in |   1 +
 4 files changed, 414 insertions(+)
 create mode 100644 libibverbs/cmd_ioctl.c
 create mode 100644 libibverbs/cmd_ioctl.h
diff mbox

Patch

diff --git a/libibverbs/CMakeLists.txt b/libibverbs/CMakeLists.txt
index 464e074e7df2ba..7c889bfe88663f 100644
--- a/libibverbs/CMakeLists.txt
+++ b/libibverbs/CMakeLists.txt
@@ -8,6 +8,7 @@  publish_headers(infiniband
   )
 
 publish_internal_headers(infiniband
+  cmd_ioctl.h
   driver.h
   kern-abi.h
   marshall.h
@@ -26,6 +27,7 @@  rdma_library(ibverbs "${CMAKE_CURRENT_BINARY_DIR}/libibverbs.map"
   # See Documentation/versioning.md
   1 1.1.${PACKAGE_VERSION}
   cmd.c
+  cmd_ioctl.c
   compat-1_0.c
   device.c
   dummy_ops.c
diff --git a/libibverbs/cmd_ioctl.c b/libibverbs/cmd_ioctl.c
new file mode 100644
index 00000000000000..722dabb565069e
--- /dev/null
+++ b/libibverbs/cmd_ioctl.c
@@ -0,0 +1,112 @@ 
+/*
+ * 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 <sys/ioctl.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);
+
+		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;
+}
+
+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->last_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)
+{
+	prepare_attrs(cmd);
+	cmd->hdr.length = sizeof(cmd->hdr) +
+		sizeof(cmd->hdr.attrs[0]) * cmd->hdr.num_attrs;
+	cmd->hdr.reserved = 0;
+
+	if (ioctl(context->cmd_fd, RDMA_VERBS_IOCTL, &cmd->hdr))
+		return errno;
+
+	finalize_attrs(cmd);
+
+	return 0;
+}
diff --git a/libibverbs/cmd_ioctl.h b/libibverbs/cmd_ioctl.h
new file mode 100644
index 00000000000000..13a1ca21c610d2
--- /dev/null
+++ b/libibverbs/cmd_ioctl.h
@@ -0,0 +1,299 @@ 
+/*
+ * 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 <stdint.h>
+#include <assert.h>
+#include <rdma/rdma_user_ioctl.h>
+#include <rdma/ib_user_ioctl_verbs.h>
+#include <infiniband/verbs.h>
+
+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
+}
+
+/*
+ * 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;
+	struct ib_uverbs_ioctl_hdr hdr;
+};
+
+/*
+ * 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,                                                 \
+		.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.
+ */
+#define DECLARE_COMMAND_BUFFER_LINK(_name, _object_id, _method_id, _num_attrs, \
+				    _link)                                     \
+	struct ibv_command_buffer _name[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;
+}
+
+/* Make the attribute optional. */
+static inline struct ib_uverbs_attr *attr_optional(struct ib_uverbs_attr *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);
+
+	assert(len <= UINT16_MAX);
+
+	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;
+}
+
+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);
+
+	assert(len <= UINT16_MAX);
+	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)))
+
+#endif
diff --git a/libibverbs/libibverbs.map.in b/libibverbs/libibverbs.map.in
index e71376a67e9206..e97548df9258ca 100644
--- a/libibverbs/libibverbs.map.in
+++ b/libibverbs/libibverbs.map.in
@@ -113,6 +113,7 @@  IBVERBS_1.1 {
 IBVERBS_PRIVATE_@IBVERBS_PABI_VERSION@ {
 	global:
 		/* These historical symbols are now private to libibverbs */
+		__ioctl_final_num_attrs;
 		_verbs_init_and_alloc_context;
 		ibv_cmd_alloc_mw;
 		ibv_cmd_alloc_pd;