/* Copyright 2019-2020 National Research Foundation (SARAO)
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file
 *
 * This file is automatically generated. Do not edit.
 */

#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <spead2/common_features.h>

#if SPEAD2_USE_IBV

#include <spead2/common_loader_ibv.h>
#include <spead2/common_loader_utils.h>
#include <spead2/common_logging.h>
#include <mutex>
#include <exception>

namespace spead2
{

static std::once_flag init_once;
static std::exception_ptr init_result;

static void init();

void ibv_ack_cq_events_stub(struct ibv_cq *cq, unsigned int nevents)
{
    (void) cq;
    (void) nevents;
    std::rethrow_exception(init_result);
}

void ibv_ack_cq_events_first(struct ibv_cq *cq, unsigned int nevents)
{
    std::call_once(init_once, init);
    return ibv_ack_cq_events(cq, nevents);
}

struct ibv_pd *ibv_alloc_pd_stub(struct ibv_context *context)
{
    (void) context;
    std::rethrow_exception(init_result);
}

struct ibv_pd *ibv_alloc_pd_first(struct ibv_context *context)
{
    std::call_once(init_once, init);
    return ibv_alloc_pd(context);
}

int ibv_close_device_stub(struct ibv_context *context)
{
    (void) context;
    std::rethrow_exception(init_result);
}

int ibv_close_device_first(struct ibv_context *context)
{
    std::call_once(init_once, init);
    return ibv_close_device(context);
}

struct ibv_comp_channel *ibv_create_comp_channel_stub(struct ibv_context *context)
{
    (void) context;
    std::rethrow_exception(init_result);
}

struct ibv_comp_channel *ibv_create_comp_channel_first(struct ibv_context *context)
{
    std::call_once(init_once, init);
    return ibv_create_comp_channel(context);
}

struct ibv_cq *ibv_create_cq_stub(struct ibv_context *context, int cqe, void *cq_context, struct ibv_comp_channel *channel, int comp_vector)
{
    (void) context;
    (void) cqe;
    (void) cq_context;
    (void) channel;
    (void) comp_vector;
    std::rethrow_exception(init_result);
}

struct ibv_cq *ibv_create_cq_first(struct ibv_context *context, int cqe, void *cq_context, struct ibv_comp_channel *channel, int comp_vector)
{
    std::call_once(init_once, init);
    return ibv_create_cq(context, cqe, cq_context, channel, comp_vector);
}

struct ibv_qp *ibv_create_qp_stub(struct ibv_pd *pd, struct ibv_qp_init_attr *qp_init_attr)
{
    (void) pd;
    (void) qp_init_attr;
    std::rethrow_exception(init_result);
}

struct ibv_qp *ibv_create_qp_first(struct ibv_pd *pd, struct ibv_qp_init_attr *qp_init_attr)
{
    std::call_once(init_once, init);
    return ibv_create_qp(pd, qp_init_attr);
}

int ibv_dealloc_pd_stub(struct ibv_pd *pd)
{
    (void) pd;
    std::rethrow_exception(init_result);
}

int ibv_dealloc_pd_first(struct ibv_pd *pd)
{
    std::call_once(init_once, init);
    return ibv_dealloc_pd(pd);
}

int ibv_dereg_mr_stub(struct ibv_mr *mr)
{
    (void) mr;
    std::rethrow_exception(init_result);
}

int ibv_dereg_mr_first(struct ibv_mr *mr)
{
    std::call_once(init_once, init);
    return ibv_dereg_mr(mr);
}

int ibv_destroy_comp_channel_stub(struct ibv_comp_channel *channel)
{
    (void) channel;
    std::rethrow_exception(init_result);
}

int ibv_destroy_comp_channel_first(struct ibv_comp_channel *channel)
{
    std::call_once(init_once, init);
    return ibv_destroy_comp_channel(channel);
}

int ibv_destroy_cq_stub(struct ibv_cq *cq)
{
    (void) cq;
    std::rethrow_exception(init_result);
}

int ibv_destroy_cq_first(struct ibv_cq *cq)
{
    std::call_once(init_once, init);
    return ibv_destroy_cq(cq);
}

int ibv_destroy_qp_stub(struct ibv_qp *qp)
{
    (void) qp;
    std::rethrow_exception(init_result);
}

int ibv_destroy_qp_first(struct ibv_qp *qp)
{
    std::call_once(init_once, init);
    return ibv_destroy_qp(qp);
}

void ibv_free_device_list_stub(struct ibv_device **list)
{
    (void) list;
    std::rethrow_exception(init_result);
}

void ibv_free_device_list_first(struct ibv_device **list)
{
    std::call_once(init_once, init);
    return ibv_free_device_list(list);
}

int ibv_get_cq_event_stub(struct ibv_comp_channel *channel, struct ibv_cq **cq, void **cq_context)
{
    (void) channel;
    (void) cq;
    (void) cq_context;
    std::rethrow_exception(init_result);
}

int ibv_get_cq_event_first(struct ibv_comp_channel *channel, struct ibv_cq **cq, void **cq_context)
{
    std::call_once(init_once, init);
    return ibv_get_cq_event(channel, cq, cq_context);
}

uint64_t ibv_get_device_guid_stub(struct ibv_device *device)
{
    (void) device;
    std::rethrow_exception(init_result);
}

uint64_t ibv_get_device_guid_first(struct ibv_device *device)
{
    std::call_once(init_once, init);
    return ibv_get_device_guid(device);
}

struct ibv_device **ibv_get_device_list_stub(int *num_devices)
{
    (void) num_devices;
    std::rethrow_exception(init_result);
}

struct ibv_device **ibv_get_device_list_first(int *num_devices)
{
    std::call_once(init_once, init);
    return ibv_get_device_list(num_devices);
}

struct ibv_context *ibv_open_device_stub(struct ibv_device *device)
{
    (void) device;
    std::rethrow_exception(init_result);
}

struct ibv_context *ibv_open_device_first(struct ibv_device *device)
{
    std::call_once(init_once, init);
    return ibv_open_device(device);
}

int ibv_modify_qp_stub(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mask)
{
    (void) qp;
    (void) attr;
    (void) attr_mask;
    std::rethrow_exception(init_result);
}

int ibv_modify_qp_first(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mask)
{
    std::call_once(init_once, init);
    return ibv_modify_qp(qp, attr, attr_mask);
}

int ibv_query_device_stub(struct ibv_context *context, struct ibv_device_attr *device_attr)
{
    (void) context;
    (void) device_attr;
    std::rethrow_exception(init_result);
}

int ibv_query_device_first(struct ibv_context *context, struct ibv_device_attr *device_attr)
{
    std::call_once(init_once, init);
    return ibv_query_device(context, device_attr);
}

struct ibv_mr *ibv_reg_mr_stub(struct ibv_pd *pd, void *addr, size_t length, int access)
{
    (void) pd;
    (void) addr;
    (void) length;
    (void) access;
    std::rethrow_exception(init_result);
}

struct ibv_mr *ibv_reg_mr_first(struct ibv_pd *pd, void *addr, size_t length, int access)
{
    std::call_once(init_once, init);
    return ibv_reg_mr(pd, addr, length, access);
}

struct ibv_mr *ibv_reg_mr_iova2_stub(struct ibv_pd *pd, void *addr, size_t length, uint64_t iova, unsigned int access)
{
    (void) pd;
    (void) addr;
    (void) length;
    (void) iova;
    (void) access;
    std::rethrow_exception(init_result);
}

struct ibv_mr *ibv_reg_mr_iova2_missing(struct ibv_pd *pd, void *addr, size_t length, uint64_t iova, unsigned int access)
{
    throw std::system_error(EOPNOTSUPP, std::system_category(), "ibv_reg_mr_iova2 not found in library");
}

bool has_ibv_reg_mr_iova2()
{
    std::call_once(init_once, init);
    return ibv_reg_mr_iova2 != ibv_reg_mr_iova2_missing;
}

struct ibv_mr *ibv_reg_mr_iova2_first(struct ibv_pd *pd, void *addr, size_t length, uint64_t iova, unsigned int access)
{
    std::call_once(init_once, init);
    return ibv_reg_mr_iova2(pd, addr, length, iova, access);
}


void (*ibv_ack_cq_events)(struct ibv_cq *cq, unsigned int nevents) = ibv_ack_cq_events_first;
struct ibv_pd *(*ibv_alloc_pd)(struct ibv_context *context) = ibv_alloc_pd_first;
int (*ibv_close_device)(struct ibv_context *context) = ibv_close_device_first;
struct ibv_comp_channel *(*ibv_create_comp_channel)(struct ibv_context *context) = ibv_create_comp_channel_first;
struct ibv_cq *(*ibv_create_cq)(struct ibv_context *context, int cqe, void *cq_context, struct ibv_comp_channel *channel, int comp_vector) = ibv_create_cq_first;
struct ibv_qp *(*ibv_create_qp)(struct ibv_pd *pd, struct ibv_qp_init_attr *qp_init_attr) = ibv_create_qp_first;
int (*ibv_dealloc_pd)(struct ibv_pd *pd) = ibv_dealloc_pd_first;
int (*ibv_dereg_mr)(struct ibv_mr *mr) = ibv_dereg_mr_first;
int (*ibv_destroy_comp_channel)(struct ibv_comp_channel *channel) = ibv_destroy_comp_channel_first;
int (*ibv_destroy_cq)(struct ibv_cq *cq) = ibv_destroy_cq_first;
int (*ibv_destroy_qp)(struct ibv_qp *qp) = ibv_destroy_qp_first;
void (*ibv_free_device_list)(struct ibv_device **list) = ibv_free_device_list_first;
int (*ibv_get_cq_event)(struct ibv_comp_channel *channel, struct ibv_cq **cq, void **cq_context) = ibv_get_cq_event_first;
uint64_t (*ibv_get_device_guid)(struct ibv_device *device) = ibv_get_device_guid_first;
struct ibv_device **(*ibv_get_device_list)(int *num_devices) = ibv_get_device_list_first;
struct ibv_context *(*ibv_open_device)(struct ibv_device *device) = ibv_open_device_first;
int (*ibv_modify_qp)(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mask) = ibv_modify_qp_first;
int (*ibv_query_device)(struct ibv_context *context, struct ibv_device_attr *device_attr) = ibv_query_device_first;
struct ibv_mr *(*ibv_reg_mr)(struct ibv_pd *pd, void *addr, size_t length, int access) = ibv_reg_mr_first;
struct ibv_mr *(*ibv_reg_mr_iova2)(struct ibv_pd *pd, void *addr, size_t length, uint64_t iova, unsigned int access) = ibv_reg_mr_iova2_first;

static void reset_stubs()
{
    ibv_ack_cq_events = ibv_ack_cq_events_stub;
    ibv_alloc_pd = ibv_alloc_pd_stub;
    ibv_close_device = ibv_close_device_stub;
    ibv_create_comp_channel = ibv_create_comp_channel_stub;
    ibv_create_cq = ibv_create_cq_stub;
    ibv_create_qp = ibv_create_qp_stub;
    ibv_dealloc_pd = ibv_dealloc_pd_stub;
    ibv_dereg_mr = ibv_dereg_mr_stub;
    ibv_destroy_comp_channel = ibv_destroy_comp_channel_stub;
    ibv_destroy_cq = ibv_destroy_cq_stub;
    ibv_destroy_qp = ibv_destroy_qp_stub;
    ibv_free_device_list = ibv_free_device_list_stub;
    ibv_get_cq_event = ibv_get_cq_event_stub;
    ibv_get_device_guid = ibv_get_device_guid_stub;
    ibv_get_device_list = ibv_get_device_list_stub;
    ibv_open_device = ibv_open_device_stub;
    ibv_modify_qp = ibv_modify_qp_stub;
    ibv_query_device = ibv_query_device_stub;
    ibv_reg_mr = ibv_reg_mr_stub;
    ibv_reg_mr_iova2 = ibv_reg_mr_iova2_stub;
}

static void init()
{
    try
    {
        dl_handle lib("libibverbs.so.1");
        ibv_ack_cq_events = reinterpret_cast<void (*)(struct ibv_cq *cq, unsigned int nevents)>(
            lib.sym("ibv_ack_cq_events"));
        ibv_alloc_pd = reinterpret_cast<struct ibv_pd *(*)(struct ibv_context *context)>(
            lib.sym("ibv_alloc_pd"));
        ibv_close_device = reinterpret_cast<int (*)(struct ibv_context *context)>(
            lib.sym("ibv_close_device"));
        ibv_create_comp_channel = reinterpret_cast<struct ibv_comp_channel *(*)(struct ibv_context *context)>(
            lib.sym("ibv_create_comp_channel"));
        ibv_create_cq = reinterpret_cast<struct ibv_cq *(*)(struct ibv_context *context, int cqe, void *cq_context, struct ibv_comp_channel *channel, int comp_vector)>(
            lib.sym("ibv_create_cq"));
        ibv_create_qp = reinterpret_cast<struct ibv_qp *(*)(struct ibv_pd *pd, struct ibv_qp_init_attr *qp_init_attr)>(
            lib.sym("ibv_create_qp"));
        ibv_dealloc_pd = reinterpret_cast<int (*)(struct ibv_pd *pd)>(
            lib.sym("ibv_dealloc_pd"));
        ibv_dereg_mr = reinterpret_cast<int (*)(struct ibv_mr *mr)>(
            lib.sym("ibv_dereg_mr"));
        ibv_destroy_comp_channel = reinterpret_cast<int (*)(struct ibv_comp_channel *channel)>(
            lib.sym("ibv_destroy_comp_channel"));
        ibv_destroy_cq = reinterpret_cast<int (*)(struct ibv_cq *cq)>(
            lib.sym("ibv_destroy_cq"));
        ibv_destroy_qp = reinterpret_cast<int (*)(struct ibv_qp *qp)>(
            lib.sym("ibv_destroy_qp"));
        ibv_free_device_list = reinterpret_cast<void (*)(struct ibv_device **list)>(
            lib.sym("ibv_free_device_list"));
        ibv_get_cq_event = reinterpret_cast<int (*)(struct ibv_comp_channel *channel, struct ibv_cq **cq, void **cq_context)>(
            lib.sym("ibv_get_cq_event"));
        ibv_get_device_guid = reinterpret_cast<uint64_t (*)(struct ibv_device *device)>(
            lib.sym("ibv_get_device_guid"));
        ibv_get_device_list = reinterpret_cast<struct ibv_device **(*)(int *num_devices)>(
            lib.sym("ibv_get_device_list"));
        ibv_open_device = reinterpret_cast<struct ibv_context *(*)(struct ibv_device *device)>(
            lib.sym("ibv_open_device"));
        ibv_modify_qp = reinterpret_cast<int (*)(struct ibv_qp *qp, struct ibv_qp_attr *attr, int attr_mask)>(
            lib.sym("ibv_modify_qp"));
        ibv_query_device = reinterpret_cast<int (*)(struct ibv_context *context, struct ibv_device_attr *device_attr)>(
            lib.sym("ibv_query_device"));
        ibv_reg_mr = reinterpret_cast<struct ibv_mr *(*)(struct ibv_pd *pd, void *addr, size_t length, int access)>(
            lib.sym("ibv_reg_mr"));
        try
        {
            ibv_reg_mr_iova2 = reinterpret_cast<struct ibv_mr *(*)(struct ibv_pd *pd, void *addr, size_t length, uint64_t iova, unsigned int access)>(
                lib.sym("ibv_reg_mr_iova2"));
        }
        catch (std::system_error &)
        {
            ibv_reg_mr_iova2 = ibv_reg_mr_iova2_missing;
        }
        // Prevent the library being closed, so that the symbols stay valid
        lib.release();
    }
    catch (std::system_error &e)
    {
        init_result = std::current_exception();
        reset_stubs();
        log_warning("could not load libibverbs.so.1: %s", e.what());
    }
}

} // namespace spead2

/* Wrappers in the global namespace. This is needed because some functions
 * call others, and so we need to provide an implementation. We limit it
 * to functions where this is known to be an issue, because the header
 * doesn't always match the man page even though there is binary
 * compatibility (e.g. on LP64 systems, unsigned long and unsigned long long
 * are both 64-bit but are considered different types).
 */

struct ibv_qp *ibv_create_qp(struct ibv_pd *pd, struct ibv_qp_init_attr *qp_init_attr)
{
    return spead2::ibv_create_qp(pd, qp_init_attr);
}

int ibv_query_device(struct ibv_context *context, struct ibv_device_attr *device_attr)
{
    return spead2::ibv_query_device(context, device_attr);
}

#endif // SPEAD2_USE_IBV
