/*
 * Copyright (c) 2007 Cisco Systems, Inc.  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 <config.h>

#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <alloca.h>

#include <util/symver.h>
#include "ibverbs.h"

struct ibv_pd_1_0 {
	struct ibv_context_1_0 *context;
	uint32_t		handle;

	struct ibv_pd	       *real_pd;
};

struct ibv_mr_1_0 {
	struct ibv_context_1_0 *context;
	struct ibv_pd_1_0      *pd;
	uint32_t		handle;
	uint32_t		lkey;
	uint32_t		rkey;

	struct ibv_mr	       *real_mr;
};

struct ibv_srq_1_0 {
	struct ibv_context_1_0 *context;
	void		       *srq_context;
	struct ibv_pd_1_0      *pd;
	uint32_t		handle;

	pthread_mutex_t		mutex;
	pthread_cond_t		cond;
	uint32_t		events_completed;

	struct ibv_srq	       *real_srq;
};

struct ibv_qp_init_attr_1_0 {
	void		       *qp_context;
	struct ibv_cq_1_0      *send_cq;
	struct ibv_cq_1_0      *recv_cq;
	struct ibv_srq_1_0     *srq;
	struct ibv_qp_cap	cap;
	enum ibv_qp_type	qp_type;
	int			sq_sig_all;
};

struct ibv_send_wr_1_0 {
	struct ibv_send_wr_1_0 *next;
	uint64_t		wr_id;
	struct ibv_sge	       *sg_list;
	int			num_sge;
	enum ibv_wr_opcode	opcode;
	int			send_flags;
	__be32			imm_data;
	union {
		struct {
			uint64_t	remote_addr;
			uint32_t	rkey;
		} rdma;
		struct {
			uint64_t	remote_addr;
			uint64_t	compare_add;
			uint64_t	swap;
			uint32_t	rkey;
		} atomic;
		struct {
			struct ibv_ah_1_0 *ah;
			uint32_t	remote_qpn;
			uint32_t	remote_qkey;
		} ud;
	} wr;
};

struct ibv_recv_wr_1_0 {
	struct ibv_recv_wr_1_0 *next;
	uint64_t		wr_id;
	struct ibv_sge	       *sg_list;
	int			num_sge;
};

struct ibv_qp_1_0 {
	struct ibv_context_1_0 *context;
	void		       *qp_context;
	struct ibv_pd_1_0      *pd;
	struct ibv_cq_1_0      *send_cq;
	struct ibv_cq_1_0      *recv_cq;
	struct ibv_srq_1_0     *srq;
	uint32_t		handle;
	uint32_t		qp_num;
	enum ibv_qp_state       state;
	enum ibv_qp_type	qp_type;

	pthread_mutex_t		mutex;
	pthread_cond_t		cond;
	uint32_t		events_completed;

	struct ibv_qp	       *real_qp;
};

struct ibv_cq_1_0 {
	struct ibv_context_1_0 *context;
	void		       *cq_context;
	uint32_t		handle;
	int			cqe;

	pthread_mutex_t		mutex;
	pthread_cond_t		cond;
	uint32_t		comp_events_completed;
	uint32_t		async_events_completed;

	struct ibv_cq	       *real_cq;
};

struct ibv_ah_1_0 {
	struct ibv_context_1_0 *context;
	struct ibv_pd_1_0      *pd;
	uint32_t		handle;

	struct ibv_ah	       *real_ah;
};

struct ibv_device_1_0 {
	void		       *obsolete_sysfs_dev;
	void		       *obsolete_sysfs_ibdev;
	struct ibv_device      *real_device; /* was obsolete driver member */
	struct _ibv_device_ops	_ops;
};

struct ibv_context_ops_1_0 {
	int			(*query_device)(struct ibv_context *context,
					      struct ibv_device_attr *device_attr);
	int			(*query_port)(struct ibv_context *context, uint8_t port_num,
					      struct ibv_port_attr *port_attr);
	struct ibv_pd *		(*alloc_pd)(struct ibv_context *context);
	int			(*dealloc_pd)(struct ibv_pd *pd);
	struct ibv_mr *		(*reg_mr)(struct ibv_pd *pd, void *addr, size_t length,
					  int access);
	int			(*dereg_mr)(struct ibv_mr *mr);
	struct ibv_cq *		(*create_cq)(struct ibv_context *context, int cqe,
					     struct ibv_comp_channel *channel,
					     int comp_vector);
	int			(*poll_cq)(struct ibv_cq_1_0 *cq, int num_entries,
					   struct ibv_wc *wc);
	int			(*req_notify_cq)(struct ibv_cq_1_0 *cq,
						 int solicited_only);
	void			(*cq_event)(struct ibv_cq *cq);
	int			(*resize_cq)(struct ibv_cq *cq, int cqe);
	int			(*destroy_cq)(struct ibv_cq *cq);
	struct ibv_srq *	(*create_srq)(struct ibv_pd *pd,
					      struct ibv_srq_init_attr *srq_init_attr);
	int			(*modify_srq)(struct ibv_srq *srq,
					      struct ibv_srq_attr *srq_attr,
					      int srq_attr_mask);
	int			(*query_srq)(struct ibv_srq *srq,
					     struct ibv_srq_attr *srq_attr);
	int			(*destroy_srq)(struct ibv_srq *srq);
	int			(*post_srq_recv)(struct ibv_srq_1_0 *srq,
						 struct ibv_recv_wr_1_0 *recv_wr,
						 struct ibv_recv_wr_1_0 **bad_recv_wr);
	struct ibv_qp *		(*create_qp)(struct ibv_pd *pd, struct ibv_qp_init_attr *attr);
	int			(*query_qp)(struct ibv_qp *qp, struct ibv_qp_attr *attr,
					    int attr_mask,
					    struct ibv_qp_init_attr *init_attr);
	int			(*modify_qp)(struct ibv_qp *qp, struct ibv_qp_attr *attr,
					     int attr_mask);
	int			(*destroy_qp)(struct ibv_qp *qp);
	int			(*post_send)(struct ibv_qp_1_0 *qp,
					     struct ibv_send_wr_1_0 *wr,
					     struct ibv_send_wr_1_0 **bad_wr);
	int			(*post_recv)(struct ibv_qp_1_0 *qp,
					     struct ibv_recv_wr_1_0 *wr,
					     struct ibv_recv_wr_1_0 **bad_wr);
	struct ibv_ah *		(*create_ah)(struct ibv_pd *pd, struct ibv_ah_attr *attr);
	int			(*destroy_ah)(struct ibv_ah *ah);
	int			(*attach_mcast)(struct ibv_qp *qp, union ibv_gid *gid,
						uint16_t lid);
	int			(*detach_mcast)(struct ibv_qp *qp, union ibv_gid *gid,
						uint16_t lid);
};

struct ibv_context_1_0 {
	struct ibv_device_1_0	       *device;
	struct ibv_context_ops_1_0	ops;
	int				cmd_fd;
	int				async_fd;
	int				num_comp_vectors;

	struct ibv_context	       *real_context; /* was abi_compat member */
};

typedef struct ibv_device *(*ibv_driver_init_func_1_1)(const char *uverbs_sys_path,
						       int abi_version);

COMPAT_SYMVER_FUNC(ibv_get_device_list, 1_0, "IBVERBS_1.0",
		   struct ibv_device_1_0 **,
		   int *num)
{
	struct ibv_device **real_list;
	struct ibv_device_1_0 **l;
	int i, n;

	real_list = ibv_get_device_list(&n);
	if (!real_list)
		return NULL;

	l = calloc(n + 2, sizeof (struct ibv_device_1_0 *));
	if (!l)
		goto free_device_list;

	l[0] = (void *) real_list;

	for (i = 0; i < n; ++i) {
		l[i + 1] = calloc(1, sizeof (struct ibv_device_1_0));
		if (!l[i + 1])
			goto fail;
		l[i + 1]->real_device = real_list[i];
	}

	if (num)
		*num = n;

	return l + 1;

fail:
	for (i = 1; i <= n; ++i)
		if (l[i])
			free(l[i]);
	free(l);

free_device_list:
	ibv_free_device_list(real_list);
	return NULL;
}

COMPAT_SYMVER_FUNC(ibv_free_device_list, 1_0, "IBVERBS_1.0",
		   void,
		   struct ibv_device_1_0 **list)
{
	struct ibv_device_1_0 **l = list;

	while (*l) {
		free(*l);
		++l;
	}

	ibv_free_device_list((void *) list[-1]);
	free(list - 1);
}

COMPAT_SYMVER_FUNC(ibv_get_device_name, 1_0, "IBVERBS_1.0",
		   const char *,
		   struct ibv_device_1_0 *device)
{
	return ibv_get_device_name(device->real_device);
}

COMPAT_SYMVER_FUNC(ibv_get_device_guid, 1_0, "IBVERBS_1.0",
		   __be64,
		   struct ibv_device_1_0 *device)
{
	return ibv_get_device_guid(device->real_device);
}

static int poll_cq_wrapper_1_0(struct ibv_cq_1_0 *cq, int num_entries,
			       struct ibv_wc *wc)
{
	return cq->context->real_context->ops.poll_cq(cq->real_cq, num_entries, wc);
}

static int req_notify_cq_wrapper_1_0(struct ibv_cq_1_0 *cq, int sol_only)
{
	return cq->context->real_context->ops.req_notify_cq(cq->real_cq, sol_only);
}

static int post_srq_recv_wrapper_1_0(struct ibv_srq_1_0 *srq, struct ibv_recv_wr_1_0 *wr,
				 struct ibv_recv_wr_1_0 **bad_wr)
{
	struct ibv_recv_wr_1_0 *w;
	struct ibv_recv_wr *real_wr, *head_wr = NULL, *tail_wr = NULL, *real_bad_wr;
	int ret;

	for (w = wr; w; w = w->next) {
		real_wr = alloca(sizeof *real_wr);
		real_wr->wr_id   = w->wr_id;
		real_wr->sg_list = w->sg_list;
		real_wr->num_sge = w->num_sge;
		real_wr->next    = NULL;
		if (tail_wr)
			tail_wr->next = real_wr;
		else
			head_wr = real_wr;

		tail_wr = real_wr;
	}

	ret = srq->context->real_context->ops.post_srq_recv(srq->real_srq, head_wr,
							    &real_bad_wr);

	if (ret) {
		for (real_wr = head_wr, w = wr;
		     real_wr;
		     real_wr = real_wr->next, w = w->next)
			if (real_wr == real_bad_wr) {
				*bad_wr = w;
				break;
			}
	}

	return ret;
}

static int post_send_wrapper_1_0(struct ibv_qp_1_0 *qp, struct ibv_send_wr_1_0 *wr,
				 struct ibv_send_wr_1_0 **bad_wr)
{
	struct ibv_send_wr_1_0 *w;
	struct ibv_send_wr *real_wr, *head_wr = NULL, *tail_wr = NULL, *real_bad_wr;
	int is_ud = qp->qp_type == IBV_QPT_UD;
	int ret;

	for (w = wr; w; w = w->next) {
		real_wr = alloca(sizeof *real_wr);
		real_wr->wr_id = w->wr_id;
		real_wr->next  = NULL;

#define TEST_SIZE_2_POINT(f1, f2)					\
		((offsetof(struct ibv_send_wr, f1) - offsetof(struct ibv_send_wr, f2)) \
		 == offsetof(struct ibv_send_wr_1_0, f1) - offsetof(struct ibv_send_wr_1_0, f2))
#define TEST_SIZE_TO_END(f1)					    \
		((sizeof(struct ibv_send_wr) - offsetof(struct ibv_send_wr, f1)) == \
		 (sizeof(struct ibv_send_wr_1_0) - offsetof(struct ibv_send_wr_1_0, f1)))

		if (TEST_SIZE_TO_END (sg_list))
			memcpy(&real_wr->sg_list, &w->sg_list, sizeof *real_wr
			       - offsetof(struct ibv_send_wr, sg_list));
		else if (TEST_SIZE_2_POINT (imm_data, sg_list) &&
			 TEST_SIZE_TO_END (wr)) {
			/* we have alignment up to wr, but padding between
			 * imm_data and wr, and we know wr itself is the
			 * same size */
			memcpy(&real_wr->sg_list, &w->sg_list,
			       offsetof(struct ibv_send_wr, imm_data) -
			       offsetof(struct ibv_send_wr, sg_list) +
			       sizeof real_wr->imm_data);
			memcpy(&real_wr->wr, &w->wr, sizeof real_wr->wr);
		} else {
			real_wr->sg_list = w->sg_list;
			real_wr->num_sge = w->num_sge;
			real_wr->opcode = w->opcode;
			real_wr->send_flags = w->send_flags;
			real_wr->imm_data = w->imm_data;
			if (TEST_SIZE_TO_END (wr))
				memcpy(&real_wr->wr, &w->wr,
				       sizeof real_wr->wr);
			else {
				real_wr->wr.atomic.remote_addr =
					w->wr.atomic.remote_addr;
				real_wr->wr.atomic.compare_add =
					w->wr.atomic.compare_add;
				real_wr->wr.atomic.swap =
					w->wr.atomic.swap;
				real_wr->wr.atomic.rkey =
					w->wr.atomic.rkey;
			}
		}

		if (is_ud)
			real_wr->wr.ud.ah = w->wr.ud.ah->real_ah;

		if (tail_wr)
			tail_wr->next = real_wr;
		else
			head_wr = real_wr;

		tail_wr = real_wr;
	}

	ret = qp->context->real_context->ops.post_send(qp->real_qp, head_wr,
						       &real_bad_wr);

	if (ret) {
		for (real_wr = head_wr, w = wr;
		     real_wr;
		     real_wr = real_wr->next, w = w->next)
			if (real_wr == real_bad_wr) {
				*bad_wr = w;
				break;
			}
	}

	return ret;
}

static int post_recv_wrapper_1_0(struct ibv_qp_1_0 *qp, struct ibv_recv_wr_1_0 *wr,
				 struct ibv_recv_wr_1_0 **bad_wr)
{
	struct ibv_recv_wr_1_0 *w;
	struct ibv_recv_wr *real_wr, *head_wr = NULL, *tail_wr = NULL, *real_bad_wr;
	int ret;

	for (w = wr; w; w = w->next) {
		real_wr = alloca(sizeof *real_wr);
		real_wr->wr_id   = w->wr_id;
		real_wr->sg_list = w->sg_list;
		real_wr->num_sge = w->num_sge;
		real_wr->next    = NULL;
		if (tail_wr)
			tail_wr->next = real_wr;
		else
			head_wr = real_wr;

		tail_wr = real_wr;
	}

	ret = qp->context->real_context->ops.post_recv(qp->real_qp, head_wr,
						       &real_bad_wr);

	if (ret) {
		for (real_wr = head_wr, w = wr;
		     real_wr;
		     real_wr = real_wr->next, w = w->next)
			if (real_wr == real_bad_wr) {
				*bad_wr = w;
				break;
			}
	}

	return ret;
}

COMPAT_SYMVER_FUNC(ibv_open_device, 1_0, "IBVERBS_1.0",
		   struct ibv_context_1_0 *,
		   struct ibv_device_1_0 *device)
{
	struct ibv_context     *real_ctx;
	struct ibv_context_1_0 *ctx;

	ctx = malloc(sizeof *ctx);
	if (!ctx)
		return NULL;

	real_ctx = ibv_open_device(device->real_device);
	if (!real_ctx) {
		free(ctx);
		return NULL;
	}

	ctx->device       = device;
	ctx->real_context = real_ctx;

	ctx->ops.poll_cq       = poll_cq_wrapper_1_0;
	ctx->ops.req_notify_cq = req_notify_cq_wrapper_1_0;
	ctx->ops.post_send     = post_send_wrapper_1_0;
	ctx->ops.post_recv     = post_recv_wrapper_1_0;
	ctx->ops.post_srq_recv = post_srq_recv_wrapper_1_0;

	return ctx;
}

COMPAT_SYMVER_FUNC(ibv_close_device, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_context_1_0 *context)
{
	int ret;

	ret = ibv_close_device(context->real_context);
	if (ret)
		return ret;

	free(context);
	return 0;
}

COMPAT_SYMVER_FUNC(ibv_get_async_event, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_context_1_0 *context,
		   struct ibv_async_event *event)
{
	int ret;

	ret = ibv_get_async_event(context->real_context, event);
	if (ret)
		return ret;

	switch (event->event_type) {
	case IBV_EVENT_CQ_ERR:
		event->element.cq = event->element.cq->cq_context;
		break;

	case IBV_EVENT_QP_FATAL:
	case IBV_EVENT_QP_REQ_ERR:
	case IBV_EVENT_QP_ACCESS_ERR:
	case IBV_EVENT_COMM_EST:
	case IBV_EVENT_SQ_DRAINED:
	case IBV_EVENT_PATH_MIG:
	case IBV_EVENT_PATH_MIG_ERR:
	case IBV_EVENT_QP_LAST_WQE_REACHED:
		event->element.qp = event->element.qp->qp_context;
		break;

	case IBV_EVENT_SRQ_ERR:
	case IBV_EVENT_SRQ_LIMIT_REACHED:
		event->element.srq = event->element.srq->srq_context;
		break;

	default:
		break;
	}

	return ret;
}

COMPAT_SYMVER_FUNC(ibv_ack_async_event, 1_0, "IBVERBS_1.0",
		   void,
		   struct ibv_async_event *event)
{
	struct ibv_async_event real_event = *event;

	switch (event->event_type) {
	case IBV_EVENT_CQ_ERR:
		real_event.element.cq =
			((struct ibv_cq_1_0 *) event->element.cq)->real_cq;
		break;

	case IBV_EVENT_QP_FATAL:
	case IBV_EVENT_QP_REQ_ERR:
	case IBV_EVENT_QP_ACCESS_ERR:
	case IBV_EVENT_COMM_EST:
	case IBV_EVENT_SQ_DRAINED:
	case IBV_EVENT_PATH_MIG:
	case IBV_EVENT_PATH_MIG_ERR:
	case IBV_EVENT_QP_LAST_WQE_REACHED:
		real_event.element.qp =
			((struct ibv_qp_1_0 *) event->element.qp)->real_qp;
		break;

	case IBV_EVENT_SRQ_ERR:
	case IBV_EVENT_SRQ_LIMIT_REACHED:
		real_event.element.srq =
			((struct ibv_srq_1_0 *) event->element.srq)->real_srq;
		break;

	default:
		break;
	}

	ibv_ack_async_event(&real_event);
}

COMPAT_SYMVER_FUNC(ibv_query_device, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_context_1_0 *context,
		   struct ibv_device_attr *device_attr)
{
	return ibv_query_device(context->real_context, device_attr);
}

COMPAT_SYMVER_FUNC(ibv_query_port, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_context_1_0 *context,
		   uint8_t port_num,
		   struct ibv_port_attr *port_attr)
{
	return ibv_query_port(context->real_context, port_num, port_attr);
}

COMPAT_SYMVER_FUNC(ibv_query_gid, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_context_1_0 *context,
		   uint8_t port_num, int index,
		   union ibv_gid *gid)
{
	return ibv_query_gid(context->real_context, port_num, index, gid);
}

COMPAT_SYMVER_FUNC(ibv_query_pkey, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_context_1_0 *context,
		   uint8_t port_num, int index,
		   __be16 *pkey)
{
	return ibv_query_pkey(context->real_context, port_num, index, pkey);
}

COMPAT_SYMVER_FUNC(ibv_alloc_pd, 1_0, "IBVERBS_1.0",
		   struct ibv_pd_1_0 *,
		   struct ibv_context_1_0 *context)
{
	struct ibv_pd *real_pd;
	struct ibv_pd_1_0 *pd;

	pd = malloc(sizeof *pd);
	if (!pd)
		return NULL;

	real_pd = ibv_alloc_pd(context->real_context);
	if (!real_pd) {
		free(pd);
		return NULL;
	}

	pd->context = context;
	pd->real_pd = real_pd;

	return pd;
}

COMPAT_SYMVER_FUNC(ibv_dealloc_pd, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_pd_1_0 *pd)
{
	int ret;

	ret = ibv_dealloc_pd(pd->real_pd);
	if (ret)
		return ret;

	free(pd);
	return 0;
}

COMPAT_SYMVER_FUNC(ibv_reg_mr, 1_0, "IBVERBS_1.0",
		   struct ibv_mr_1_0 *,
		   struct ibv_pd_1_0 *pd, void *addr, size_t length,
		   int access)
{
	struct ibv_mr *real_mr;
	struct ibv_mr_1_0 *mr;

	mr = malloc(sizeof *mr);
	if (!mr)
		return NULL;

	real_mr = ibv_reg_mr(pd->real_pd, addr, length, access);
	if (!real_mr) {
		free(mr);
		return NULL;
	}

	mr->context = pd->context;
	mr->pd      = pd;
	mr->lkey    = real_mr->lkey;
	mr->rkey    = real_mr->rkey;
	mr->real_mr = real_mr;

	return mr;
}

COMPAT_SYMVER_FUNC(ibv_dereg_mr, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_mr_1_0 *mr)
{
	int ret;

	ret = ibv_dereg_mr(mr->real_mr);
	if (ret)
		return ret;

	free(mr);
	return 0;
}

COMPAT_SYMVER_FUNC(ibv_create_cq, 1_0, "IBVERBS_1.0",
		   struct ibv_cq_1_0 *,
		   struct ibv_context_1_0 *context, int cqe, void *cq_context,
		   struct ibv_comp_channel *channel, int comp_vector)
{
	struct ibv_cq *real_cq;
	struct ibv_cq_1_0 *cq;

	cq = malloc(sizeof *cq);
	if (!cq)
		return NULL;

	real_cq = ibv_create_cq(context->real_context, cqe, cq_context,
				channel, comp_vector);
	if (!real_cq) {
		free(cq);
		return NULL;
	}

	cq->context    = context;
	cq->cq_context = cq_context;
	cq->cqe        = cqe;
	cq->real_cq    = real_cq;

	real_cq->cq_context = cq;

	return cq;
}

COMPAT_SYMVER_FUNC(ibv_resize_cq, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_cq_1_0 *cq, int cqe)
{
	return ibv_resize_cq(cq->real_cq, cqe);
}

COMPAT_SYMVER_FUNC(ibv_destroy_cq, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_cq_1_0 *cq)
{
	int ret;

	ret = ibv_destroy_cq(cq->real_cq);
	if (ret)
		return ret;

	free(cq);
	return 0;
}

COMPAT_SYMVER_FUNC(ibv_get_cq_event, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_comp_channel *channel,
		   struct ibv_cq_1_0 **cq,
		   void **cq_context)
{
	struct ibv_cq *real_cq;
	void *cq_ptr;
	int ret;

	ret = ibv_get_cq_event(channel, &real_cq, &cq_ptr);
	if (ret)
		return ret;

	*cq         = cq_ptr;
	*cq_context = (*cq)->cq_context;

	return 0;
}

COMPAT_SYMVER_FUNC(ibv_ack_cq_events, 1_0, "IBVERBS_1.0",
		   void,
		   struct ibv_cq_1_0 *cq,
		   unsigned int nevents)
{
	ibv_ack_cq_events(cq->real_cq, nevents);
}

COMPAT_SYMVER_FUNC(ibv_create_srq, 1_0, "IBVERBS_1.0",
		   struct ibv_srq_1_0 *,
		   struct ibv_pd_1_0 *pd,
		   struct ibv_srq_init_attr *srq_init_attr)
{
	struct ibv_srq *real_srq;
	struct ibv_srq_1_0 *srq;

	srq = malloc(sizeof *srq);
	if (!srq)
		return NULL;

	real_srq = ibv_create_srq(pd->real_pd, srq_init_attr);
	if (!real_srq) {
		free(srq);
		return NULL;
	}

	srq->context     = pd->context;
	srq->srq_context = srq_init_attr->srq_context;
	srq->pd          = pd;
	srq->real_srq    = real_srq;

	real_srq->srq_context = srq;

	return srq;
}

COMPAT_SYMVER_FUNC(ibv_modify_srq, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_srq_1_0 *srq,
		   struct ibv_srq_attr *srq_attr,
		   int srq_attr_mask)
{
	return ibv_modify_srq(srq->real_srq, srq_attr, srq_attr_mask);
}

COMPAT_SYMVER_FUNC(ibv_query_srq, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_srq_1_0 *srq,
		   struct ibv_srq_attr *srq_attr)
{
	return ibv_query_srq(srq->real_srq, srq_attr);
}

COMPAT_SYMVER_FUNC(ibv_destroy_srq, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_srq_1_0 *srq)
{
	int ret;

	ret = ibv_destroy_srq(srq->real_srq);
	if (ret)
		return ret;

	free(srq);
	return 0;
}

COMPAT_SYMVER_FUNC(ibv_create_qp, 1_0, "IBVERBS_1.0",
		   struct ibv_qp_1_0 *,
		   struct ibv_pd_1_0 *pd,
		   struct ibv_qp_init_attr_1_0 *qp_init_attr)
{
	struct ibv_qp *real_qp;
	struct ibv_qp_1_0 *qp;
	struct ibv_qp_init_attr real_init_attr;

	qp = malloc(sizeof *qp);
	if (!qp)
		return NULL;

	real_init_attr.qp_context = qp_init_attr->qp_context;
	real_init_attr.send_cq    = qp_init_attr->send_cq->real_cq;
	real_init_attr.recv_cq    = qp_init_attr->recv_cq->real_cq;
	real_init_attr.srq        = qp_init_attr->srq ?
		qp_init_attr->srq->real_srq : NULL;
	real_init_attr.cap        = qp_init_attr->cap;
	real_init_attr.qp_type    = qp_init_attr->qp_type;
	real_init_attr.sq_sig_all = qp_init_attr->sq_sig_all;

	real_qp = ibv_create_qp(pd->real_pd, &real_init_attr);
	if (!real_qp) {
		free(qp);
		return NULL;
	}

	qp->context    = pd->context;
	qp->qp_context = qp_init_attr->qp_context;
	qp->pd         = pd;
	qp->send_cq    = qp_init_attr->send_cq;
	qp->recv_cq    = qp_init_attr->recv_cq;
	qp->srq        = qp_init_attr->srq;
	qp->qp_type    = qp_init_attr->qp_type;
	qp->qp_num     = real_qp->qp_num;
	qp->real_qp    = real_qp;

	qp_init_attr->cap = real_init_attr.cap;

	real_qp->qp_context = qp;

	return qp;
}

COMPAT_SYMVER_FUNC(ibv_query_qp, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_qp_1_0 *qp, struct ibv_qp_attr *attr,
		   int attr_mask, struct ibv_qp_init_attr_1_0 *init_attr)
{
	struct ibv_qp_init_attr real_init_attr;
	int ret;

	ret = ibv_query_qp(qp->real_qp, attr, attr_mask, &real_init_attr);
	if (ret)
		return ret;

	init_attr->qp_context = qp->qp_context;
	init_attr->send_cq    = real_init_attr.send_cq->cq_context;
	init_attr->recv_cq    = real_init_attr.recv_cq->cq_context;
	init_attr->srq        = real_init_attr.srq->srq_context;
	init_attr->qp_type    = real_init_attr.qp_type;
	init_attr->cap        = real_init_attr.cap;
	init_attr->sq_sig_all = real_init_attr.sq_sig_all;

	return 0;
}

COMPAT_SYMVER_FUNC(ibv_modify_qp, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_qp_1_0 *qp,
		   struct ibv_qp_attr *attr,
		   int attr_mask)
{
	return ibv_modify_qp(qp->real_qp, attr, attr_mask);
}

COMPAT_SYMVER_FUNC(ibv_destroy_qp, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_qp_1_0 *qp)
{
	int ret;

	ret = ibv_destroy_qp(qp->real_qp);
	if (ret)
		return ret;

	free(qp);
	return 0;
}

COMPAT_SYMVER_FUNC(ibv_create_ah, 1_0, "IBVERBS_1.0",
		   struct ibv_ah_1_0 *,
		   struct ibv_pd_1_0 *pd, struct ibv_ah_attr *attr)
{
	struct ibv_ah *real_ah;
	struct ibv_ah_1_0 *ah;

	ah = malloc(sizeof *ah);
	if (!ah)
		return NULL;

	real_ah = ibv_create_ah(pd->real_pd, attr);
	if (!real_ah) {
		free(ah);
		return NULL;
	}

	ah->context = pd->context;
	ah->pd      = pd;
	ah->real_ah = real_ah;

	return ah;
}

COMPAT_SYMVER_FUNC(ibv_destroy_ah, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_ah_1_0 *ah)
{
	int ret;

	ret = ibv_destroy_ah(ah->real_ah);
	if (ret)
		return ret;

	free(ah);
	return 0;
}

COMPAT_SYMVER_FUNC(ibv_attach_mcast, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_qp_1_0 *qp, union ibv_gid *gid, uint16_t lid)
{
	return ibv_attach_mcast(qp->real_qp, gid, lid);
}

COMPAT_SYMVER_FUNC(ibv_detach_mcast, 1_0, "IBVERBS_1.0",
		   int,
		   struct ibv_qp_1_0 *qp, union ibv_gid *gid, uint16_t lid)
{
	return ibv_detach_mcast(qp->real_qp, gid, lid);
}

COMPAT_SYMVER_FUNC(ibv_register_driver, 1_1, "IBVERBS_1.1",
		   void,
		   const char *name, ibv_driver_init_func_1_1 init_func)
{
	/* The driver interface is private as of rdma-core 13. This stub is
	 * left to preserve dynamic-link compatibility with old libfabrics
	 * usnic providers which use this function only to suppress a fprintf
	 * in old versions of libibverbs. */
}
