/*
 * 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_WRITE_H
#define __INFINIBAND_VERBS_WRITE_H

#include <infiniband/cmd_ioctl.h>
#include <infiniband/driver.h>
#include <rdma/ib_user_verbs.h>
#include <rdma/ib_user_ioctl_cmds.h>

#include <stdbool.h>

void *_write_get_req(struct ibv_command_buffer *link,
		     struct ib_uverbs_cmd_hdr *onstack, size_t size);
void *_write_get_req_ex(struct ibv_command_buffer *link, struct ex_hdr *onstack,
			size_t size);
void *_write_get_resp(struct ibv_command_buffer *link,
		      struct ib_uverbs_cmd_hdr *hdr, void *onstack,
		      size_t resp_size);
void *_write_get_resp_ex(struct ibv_command_buffer *link,
			 struct ex_hdr *hdr, void *onstack,
			 size_t resp_size);

/*
 * This macro creates 'req' and 'resp' pointers in the local stack frame that
 * point to the core code write command structures patterned off _pattern.
 *
 * This should be done before calling execute_write_bufs
 */
#define DECLARE_LEGACY_UHW_BUFS(_link, _enum)                                  \
	IBV_ABI_REQ(_enum) __req_onstack;                                      \
	IBV_KABI_RESP(_enum) __resp_onstack;                                   \
	IBV_KABI_REQ(_enum) *req =                                             \
		_write_get_req(_link, &__req_onstack.hdr, sizeof(*req));       \
	IBV_KABI_RESP(_enum) *resp = ({                                        \
		void *_resp = _write_get_resp(                                 \
			_link,                                                 \
			&container_of(req, IBV_ABI_REQ(_enum), core_payload)   \
				 ->hdr,                                        \
			&__resp_onstack, sizeof(*resp));                       \
		_resp;                                                         \
	})

#define DECLARE_LEGACY_UHW_BUFS_EX(_link, _enum)                               \
	IBV_ABI_REQ(_enum) __req_onstack;                                      \
	IBV_KABI_RESP(_enum) __resp_onstack;                                   \
	IBV_KABI_REQ(_enum) *req =                                             \
		_write_get_req_ex(_link, &__req_onstack.hdr, sizeof(*req));    \
	IBV_KABI_RESP(_enum) *resp = _write_get_resp_ex(                       \
		_link,                                                         \
		&container_of(req, IBV_ABI_REQ(_enum), core_payload)->hdr,     \
		&__resp_onstack, sizeof(*resp))

/*
 * This macro is used to implement the compatibility command call wrappers.
 * Compatibility calls do not accept a command_buffer, and cannot use the new
 * attribute id mechanism. They accept the legacy kern-abi.h structs that have
 * the embedded header.
 */
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);
#define DECLARE_CMD_BUFFER_COMPAT(_name, _object_id, _method_id, cmd,          \
				  cmd_size, resp, resp_size)                   \
	DECLARE_COMMAND_BUFFER(_name, _object_id, _method_id, 2);              \
	_write_set_uhw(_name, cmd, sizeof(*cmd), cmd_size, resp,               \
		       sizeof(*resp), resp_size)

#define DECLARE_CMD_BUFFER_LINK_COMPAT(_name, _object_id, _method_id,	       \
				       _link, cmd, cmd_size,		       \
				       resp, resp_size)			       \
	DECLARE_COMMAND_BUFFER_LINK(_name, _object_id, _method_id, 2, _link);  \
	_write_set_uhw(_name, cmd, sizeof(*cmd), cmd_size, resp,               \
		       sizeof(*resp), resp_size)

/*
 * The fallback scheme keeps track of which ioctls succeed in a per-context
 * bitmap. If ENOTTY or EPROTONOSUPPORT is seen then the ioctl is never
 * retried.
 *
 * cmd_name should be the name of the function op from verbs_context_ops
 * that is being implemented.
 */
#define _CMD_BIT(cmd_name)                                                     \
	(offsetof(struct verbs_context_ops, cmd_name) / sizeof(void *))

enum write_fallback { TRY_WRITE, TRY_WRITE_EX, ERROR, SUCCESS };

/*
 * This bitmask indicate the required behavior of execute_ioctl_fallback when
 * the ioctl is not supported. It is a priority list where the highest set bit
 * takes precedence. This approach simplifies the typical required control
 * flow of the user.
 */
static inline void fallback_require_ex(struct ibv_command_buffer *cmdb)
{
	cmdb->fallback_require_ex = 1;
}

static inline void fallback_require_ioctl(struct ibv_command_buffer *cmdb)
{
	cmdb->fallback_ioctl_only = 1;
}

enum write_fallback _check_legacy(struct ibv_command_buffer *cmdb, int *ret);

enum write_fallback _execute_ioctl_fallback(struct ibv_context *ctx,
					    unsigned int cmd_bit,
					    struct ibv_command_buffer *cmdb,
					    int *ret);

#define execute_ioctl_fallback(ctx, cmd_name, cmdb, ret)                       \
	_execute_ioctl_fallback(ctx, _CMD_BIT(cmd_name), cmdb, ret)

/*
 * For write() only commands that have fixed core structures and may take uhw
 * driver data. The last arguments are the same ones passed into the typical
 * ibv_cmd_* function. execute_cmd_write deduces the length of the core
 * structure based on the KABI struct linked to the enum op code.
 */
int _execute_cmd_write(struct ibv_context *ctx, unsigned int write_method,
		       void *req, size_t core_req_size,
		       size_t req_size, void *resp, size_t core_resp_size,
		       size_t resp_size);
#define execute_cmd_write(ctx, enum, cmd, cmd_size, resp, resp_size)           \
	({                                                                     \
		(cmd)->core_payload.response = ioctl_ptr_to_u64(resp);         \
		_execute_cmd_write(                                            \
			ctx, enum, cmd + check_type(cmd, IBV_ABI_REQ(enum) *), \
			sizeof(*(cmd)), cmd_size,                              \
			resp + check_type(resp, IBV_KABI_RESP(enum) *),        \
			sizeof(*(resp)), resp_size);                           \
	})

/* For write() commands that have no response */
#define execute_cmd_write_req(ctx, enum, cmd, cmd_size)                        \
	({                                                                     \
		static_assert(sizeof(IBV_KABI_RESP(enum)) == 0,                \
			      "Method has a response!");                       \
		_execute_cmd_write(ctx, enum,                                  \
				   cmd + check_type(cmd, IBV_ABI_REQ(enum) *), \
				   sizeof(*(cmd)), cmd_size, NULL, 0, 0);      \
	})

/*
 * Execute a write command that does not have a uhw component. The cmd_size
 * and resp_size are the lengths of the core structure. This version is only
 * needed if the core structure ends in a flex array, as the internal sizeof()
 * in execute_cmd_write() will give the wrong size.
 */
#define execute_cmd_write_no_uhw(ctx, enum, cmd, cmd_size, resp, resp_size)    \
	({                                                                     \
		(cmd)->core_payload.response = ioctl_ptr_to_u64(resp);         \
		_execute_cmd_write(                                            \
			ctx, enum, cmd + check_type(cmd, IBV_ABI_REQ(enum) *), \
			cmd_size, cmd_size,                                    \
			resp + check_type(resp, IBV_KABI_RESP(enum) *),        \
			resp_size, resp_size);                                 \
	})

/*
 * For users of DECLARE_LEGACY_UHW_BUFS, in this case the machinery has
 * already stored the full req/resp length in the hdr.
 */
#define execute_write_bufs(ctx, enum, req, resp)                               \
	({                                                                     \
		IBV_ABI_REQ(enum) *_hdr =                                      \
			container_of(req, IBV_ABI_REQ(enum), core_payload);    \
		execute_cmd_write(ctx, enum, _hdr, _hdr->hdr.in_words * 4,     \
				  resp, _hdr->hdr.out_words * 4);              \
	})

/*
 * For write() commands that use the _ex protocol. _full allows the caller to
 * specify all 4 sizes directly. This version is used when the core structs
 * end in a flex array. The normal and req versions are similar to write() and
 * deduce the length of the core struct from the enum.
 */
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);
#define execute_cmd_write_ex_full(ctx, enum, cmd, core_cmd_size, cmd_size,     \
				  resp, core_resp_size, resp_size)             \
	_execute_cmd_write_ex(                                                 \
		ctx, enum, &(cmd)->hdr + check_type(cmd, IBV_ABI_REQ(enum) *), \
		core_cmd_size, cmd_size,                                       \
		resp + check_type(resp, IBV_KABI_RESP(enum) *),                \
		core_resp_size, resp_size)
#define execute_cmd_write_ex(ctx, enum, cmd, cmd_size, resp, resp_size)        \
	execute_cmd_write_ex_full(ctx, enum, cmd, sizeof(*(cmd)), cmd_size,    \
				  resp, sizeof(*(resp)), resp_size)
#define execute_cmd_write_ex_req(ctx, enum, cmd, cmd_size)                     \
	({                                                                     \
		static_assert(sizeof(IBV_KABI_RESP(enum)) == 0,                \
			      "Method has a response!");                       \
		_execute_cmd_write_ex(                                         \
			ctx, enum,                                             \
			&(cmd)->hdr + check_type(cmd, IBV_ABI_REQ(enum) *),    \
			sizeof(*(cmd)), cmd_size, NULL, 0, 0);                 \
	})

/* For users of DECLARE_LEGACY_UHW_BUFS_EX */
#define execute_write_bufs_ex(ctx, enum, req, resp)                            \
	({                                                                     \
		IBV_ABI_REQ(enum) *_hdr =                                      \
			container_of(req, IBV_ABI_REQ(enum), core_payload);    \
		execute_cmd_write_ex(                                          \
			ctx, enum, _hdr,                                       \
			sizeof(*_hdr) +                                        \
				_hdr->hdr.ex_hdr.provider_in_words * 8,        \
			resp,                                                  \
			sizeof(*(resp)) +                                      \
				_hdr->hdr.ex_hdr.provider_out_words * 8);      \
	})

/*
 * These two macros are used only with execute_ioctl_fallback - they allow the
 * IOCTL code to be elided by the compiler when disabled.
 */
#define DECLARE_FBCMD_BUFFER DECLARE_COMMAND_BUFFER_LINK

/*
 * Munge the macros above to remove certain paths during compilation based on
 * the cmake flag.
 */
#if VERBS_IOCTL_ONLY
static inline enum write_fallback
_execute_ioctl_only(struct ibv_context *context, struct ibv_command_buffer *cmd,
		    int *ret)
{
	*ret = execute_ioctl(context, cmd);
	if (*ret)
		return ERROR;

	return SUCCESS;
}

#undef execute_ioctl_fallback
#define execute_ioctl_fallback(ctx, cmd_name, cmdb, ret)                       \
	_execute_ioctl_only(ctx, cmdb, ret)

#undef execute_write_bufs
static inline int execute_write_bufs(struct ibv_context *ctx,
				     unsigned int write_command, void *req,
				     void *resp)
{
	return ENOSYS;
}

#undef execute_write_bufs_ex
static inline int execute_write_bufs_ex(struct ibv_context *ctx,
					unsigned int write_command, void *req,
					void *resp)
{
	return ENOSYS;
}

#endif

#if VERBS_WRITE_ONLY
static inline enum write_fallback
_execute_write_only(struct ibv_context *context, struct ibv_command_buffer *cmd,
		    int *ret)
{
	/*
	 * write only still has the command buffer, and the command buffer
	 * carries the fallback guidance that we need to inspect. This is
	 * written in this odd way so the compiler knows that SUCCESS is not a
	 * possible return and optimizes accordingly.
	 */
	switch (_check_legacy(cmd, ret)) {
	case TRY_WRITE:
		return TRY_WRITE;
	case TRY_WRITE_EX:
		return TRY_WRITE_EX;
	default:
		return ERROR;
	}
}

#undef execute_ioctl_fallback
#define execute_ioctl_fallback(ctx, cmd_name, cmdb, ret)                       \
	_execute_write_only(ctx, cmdb, ret)

#undef DECLARE_FBCMD_BUFFER
#define DECLARE_FBCMD_BUFFER(_name, _object_id, _method_id, _num_attrs, _link) \
	struct ibv_command_buffer _name[1] = {                                 \
		{                                                              \
			.next = _link,                                         \
			.uhw_in_idx = _UHW_NO_INDEX,                           \
			.uhw_out_idx = _UHW_NO_INDEX,                          \
		},                                                             \
	}

#endif

extern bool verbs_allow_disassociate_destroy;

/*
 * Return true if 'ret' indicates that a destroy operation has failed
 * and the function should exit. If the kernel destroy failure is being
 * ignored then this will set ret to 0, so the calling function appears to succeed.
 */
static inline bool verbs_is_destroy_err(int *ret)
{
	if (*ret == EIO && verbs_allow_disassociate_destroy) {
		*ret = 0;
		return true;
	}

	return *ret != 0;
}

#endif
