/*
 * BSD LICENSE
 *
 * Copyright(c) 2014-2023 Intel Corporation. All rights reserved.
 *
 * 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.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @brief Set of utility functions to operate on Platform QoS (pqos) data
 *        structures.
 *
 * These functions need no synchronization mechanisms.
 *
 */

#include "utils.h"

#include "cap.h"
#include "cpuinfo.h"
#include "pqos.h"

#include <stdlib.h>
#include <string.h>

#define TOPO_OBJ_SOCKET     0
#define TOPO_OBJ_L2_CLUSTER 2
#define TOPO_OBJ_L3_CLUSTER 3

int
_pqos_utils_init(int interface)
{
        UNUSED_PARAM(interface);

        return PQOS_RETVAL_OK;
}

unsigned *
pqos_cpu_get_mba_ids(const struct pqos_cpuinfo *cpu, unsigned *count)
{
        unsigned mba_id_count = 0, i = 0;
        unsigned *mba_ids = NULL;

        ASSERT(cpu != NULL);
        ASSERT(count != NULL);
        if (cpu == NULL || count == NULL)
                return NULL;

        mba_ids = (unsigned *)malloc(sizeof(mba_ids[0]) * cpu->num_cores);
        if (mba_ids == NULL)
                return NULL;

        for (i = 0; i < cpu->num_cores; i++) {
                unsigned j = 0;

                /**
                 * Check if this mba id is already on the \a mbas list
                 */
                for (j = 0; j < mba_id_count && mba_id_count > 0; j++)
                        if (cpu->cores[i].mba_id == mba_ids[j])
                                break;

                if (j >= mba_id_count || mba_id_count == 0) {
                        /**
                         * This mba_id wasn't reported before
                         */
                        mba_ids[mba_id_count++] = cpu->cores[i].mba_id;
                }
        }

        *count = mba_id_count;
        return mba_ids;
}

unsigned *
pqos_cpu_get_smba_ids(const struct pqos_cpuinfo *cpu, unsigned *count)
{
        unsigned smba_id_count = 0, i = 0;
        unsigned *smba_ids = NULL;

        ASSERT(cpu != NULL);
        ASSERT(count != NULL);
        if (cpu == NULL || count == NULL)
                return NULL;

        smba_ids = (unsigned *)malloc(sizeof(smba_ids[0]) * cpu->num_cores);
        if (smba_ids == NULL)
                return NULL;

        for (i = 0; i < cpu->num_cores; i++) {
                unsigned j = 0;

                /**
                 * Check if this smba id is already on the \a mbas list
                 */
                for (j = 0; j < smba_id_count && smba_id_count > 0; j++)
                        if (cpu->cores[i].smba_id == smba_ids[j])
                                break;

                if (j >= smba_id_count || smba_id_count == 0) {
                        /**
                         * This smba_id wasn't reported before
                         */
                        smba_ids[smba_id_count++] = cpu->cores[i].smba_id;
                }
        }

        *count = smba_id_count;
        return smba_ids;
}

unsigned *
pqos_cpu_get_l3cat_ids(const struct pqos_cpuinfo *cpu, unsigned *count)
{
        unsigned l3cat_count = 0, i = 0;
        unsigned *l3cat_ids = NULL;

        ASSERT(cpu != NULL);
        ASSERT(count != NULL);
        if (cpu == NULL || count == NULL)
                return NULL;

        l3cat_ids = (unsigned *)malloc(sizeof(l3cat_ids[0]) * cpu->num_cores);
        if (l3cat_ids == NULL)
                return NULL;

        for (i = 0; i < cpu->num_cores; i++) {
                unsigned j = 0;

                /**
                 * Check if this l3cat id is already on the \a l3cat_ids list
                 */
                for (j = 0; j < l3cat_count && l3cat_count > 0; j++)
                        if (cpu->cores[i].l3cat_id == l3cat_ids[j])
                                break;

                if (j >= l3cat_count || l3cat_count == 0) {
                        /**
                         * This l3cat_id wasn't reported before
                         */
                        l3cat_ids[l3cat_count++] = cpu->cores[i].l3cat_id;
                }
        }

        *count = l3cat_count;
        return l3cat_ids;
}

unsigned *
pqos_cpu_get_sockets(const struct pqos_cpuinfo *cpu, unsigned *count)
{
        unsigned scount = 0, i = 0;
        unsigned *sockets = NULL;

        ASSERT(cpu != NULL);
        ASSERT(count != NULL);
        if (cpu == NULL || count == NULL)
                return NULL;

        sockets = (unsigned *)malloc(sizeof(sockets[0]) * cpu->num_cores);
        if (sockets == NULL)
                return NULL;

        for (i = 0; i < cpu->num_cores; i++) {
                unsigned j = 0;

                /**
                 * Check if this socket id is already on the \a sockets list
                 */
                for (j = 0; j < scount && scount > 0; j++)
                        if (cpu->cores[i].socket == sockets[j])
                                break;

                if (j >= scount || scount == 0) {
                        /**
                         * This socket wasn't reported before
                         */
                        sockets[scount++] = cpu->cores[i].socket;
                }
        }

        *count = scount;
        return sockets;
}

unsigned *
pqos_cpu_get_numa(const struct pqos_cpuinfo *cpu, unsigned *count)
{
        unsigned ncount = 0, i = 0;
        unsigned *numa = NULL;

        if (cpu == NULL || count == NULL)
                return NULL;
        numa = (unsigned *)malloc(sizeof(numa[0]) * cpu->num_cores);
        if (numa == NULL)
                return NULL;
        for (i = 0; i < cpu->num_cores; i++) {
                unsigned j = 0;

                /**
                 * Check if this numa id is already on the \a numa list
                 */
                for (j = 0; j < ncount; j++)
                        if (cpu->cores[i].numa == numa[j])
                                break;

                if (j >= ncount || ncount == 0) {
                        /**
                         * This socket wasn't reported before
                         */
                        numa[ncount++] = cpu->cores[i].numa;
                }
        }
        *count = ncount;
        return numa;
}

unsigned *
pqos_cpu_get_l2ids(const struct pqos_cpuinfo *cpu, unsigned *count)
{
        unsigned l2count = 0, i = 0;
        unsigned *l2ids = NULL;

        ASSERT(cpu != NULL);
        ASSERT(count != NULL);
        if (cpu == NULL || count == NULL)
                return NULL;

        l2ids = (unsigned *)malloc(sizeof(l2ids[0]) * cpu->num_cores);
        if (l2ids == NULL)
                return NULL;

        for (i = 0; i < cpu->num_cores; i++) {
                unsigned j = 0;

                /**
                 * Check if this L2 id is already on the list
                 */
                for (j = 0; j < l2count && l2count > 0; j++)
                        if (cpu->cores[i].l2_id == l2ids[j])
                                break;

                if (j >= l2count || l2count == 0) {
                        /**
                         * This l2id wasn't reported before
                         */
                        l2ids[l2count++] = cpu->cores[i].l2_id;
                }
        }

        *count = l2count;
        return l2ids;
}

/**
 * @brief Creates list of cores belonging to given topology object
 *
 * @param [in] cpu CPU topology
 * @param [in] type CPU topology object type to search cores for
 *             TOPO_OBJ_SOCKET - sockets
 *             TOPO_OBJ_L2_CLUSTER - L2 cache clusters
 *             TOPO_OBJ_L3_CLUSTER - L3 cache clusters
 * @param [in] id CPU topology object ID to search cores for
 * @param [out] count place to put number of objects found
 *
 * @return Pointer to list of cores for given topology object
 * @retval NULL on error or if no core found
 */
static unsigned *
__get_cores_per_topology_obj(const struct pqos_cpuinfo *cpu,
                             const int type,
                             const unsigned id,
                             unsigned *count)
{
        unsigned num = 0, i = 0;
        unsigned *core_list = NULL;

        ASSERT(cpu != NULL);
        ASSERT(count != NULL);
        if (cpu == NULL || count == NULL)
                return NULL;

        core_list = (unsigned *)malloc(cpu->num_cores * sizeof(core_list[0]));
        if (core_list == NULL)
                return NULL;

        for (i = 0; i < cpu->num_cores; i++)
                if ((type == TOPO_OBJ_L3_CLUSTER &&
                     id == cpu->cores[i].l3_id) ||
                    (type == TOPO_OBJ_L2_CLUSTER &&
                     id == cpu->cores[i].l2_id) ||
                    (type == TOPO_OBJ_SOCKET && id == cpu->cores[i].socket))
                        core_list[num++] = cpu->cores[i].lcore;

        if (num == 0) {
                free(core_list);
                return NULL;
        }

        *count = num;
        return core_list;
}

unsigned *
pqos_cpu_get_cores_l3id(const struct pqos_cpuinfo *cpu,
                        const unsigned l3_id,
                        unsigned *count)
{
        return __get_cores_per_topology_obj(cpu, TOPO_OBJ_L3_CLUSTER, l3_id,
                                            count);
}

unsigned *
pqos_cpu_get_cores(const struct pqos_cpuinfo *cpu,
                   const unsigned socket,
                   unsigned *count)
{
        return __get_cores_per_topology_obj(cpu, TOPO_OBJ_SOCKET, socket,
                                            count);
}

const struct pqos_coreinfo *
pqos_cpu_get_core_info(const struct pqos_cpuinfo *cpu, unsigned lcore)
{
        unsigned i;

        ASSERT(cpu != NULL);

        if (cpu == NULL)
                return NULL;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].lcore == lcore)
                        return &cpu->cores[i];

        return NULL;
}

int
pqos_cpu_get_one_core(const struct pqos_cpuinfo *cpu,
                      const unsigned socket,
                      unsigned *lcore)
{
        unsigned i = 0;

        ASSERT(cpu != NULL);
        ASSERT(lcore != NULL);

        if (cpu == NULL || lcore == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].socket == socket) {
                        *lcore = cpu->cores[i].lcore;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_get_one_by_numaid(const struct pqos_cpuinfo *cpu,
                           const unsigned numaid,
                           unsigned *lcore)
{
        unsigned i = 0;

        ASSERT(cpu != NULL);
        ASSERT(lcore != NULL);

        if (cpu == NULL || lcore == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].numa == numaid) {
                        *lcore = cpu->cores[i].lcore;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_get_one_by_l3cat_id(const struct pqos_cpuinfo *cpu,
                             const unsigned l3cat_id,
                             unsigned *lcore)
{
        unsigned i = 0;

        ASSERT(cpu != NULL);
        ASSERT(lcore != NULL);

        if (cpu == NULL || lcore == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].l3cat_id == l3cat_id) {
                        *lcore = cpu->cores[i].lcore;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_get_one_by_mba_id(const struct pqos_cpuinfo *cpu,
                           const unsigned mba_id,
                           unsigned *lcore)
{
        unsigned i = 0;

        ASSERT(cpu != NULL);
        ASSERT(lcore != NULL);

        if (cpu == NULL || lcore == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].mba_id == mba_id) {
                        *lcore = cpu->cores[i].lcore;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_get_one_by_smba_id(const struct pqos_cpuinfo *cpu,
                            const unsigned smba_id,
                            unsigned *lcore)
{
        unsigned i = 0;

        ASSERT(cpu != NULL);
        ASSERT(lcore != NULL);

        if (cpu == NULL || lcore == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].smba_id == smba_id) {
                        *lcore = cpu->cores[i].lcore;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_get_one_by_l2id(const struct pqos_cpuinfo *cpu,
                         const unsigned l2id,
                         unsigned *lcore)
{
        unsigned i = 0;

        ASSERT(cpu != NULL);
        ASSERT(lcore != NULL);

        if (cpu == NULL || lcore == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].l2_id == l2id) {
                        *lcore = cpu->cores[i].lcore;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_check_core(const struct pqos_cpuinfo *cpu, const unsigned lcore)
{
        unsigned i;

        ASSERT(cpu != NULL);
        if (cpu == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].lcore == lcore)
                        return PQOS_RETVAL_OK;

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_get_socketid(const struct pqos_cpuinfo *cpu,
                      const unsigned lcore,
                      unsigned *socket)
{
        unsigned i = 0;

        if (cpu == NULL || socket == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].lcore == lcore) {
                        *socket = cpu->cores[i].socket;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_get_numaid(const struct pqos_cpuinfo *cpu,
                    const unsigned lcore,
                    unsigned *numa)
{
        unsigned i = 0;

        if (cpu == NULL || numa == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].lcore == lcore) {
                        *numa = cpu->cores[i].numa;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cpu_get_clusterid(const struct pqos_cpuinfo *cpu,
                       const unsigned lcore,
                       unsigned *cluster)
{
        unsigned i = 0;

        if (cpu == NULL || cluster == NULL)
                return PQOS_RETVAL_PARAM;

        for (i = 0; i < cpu->num_cores; i++)
                if (cpu->cores[i].lcore == lcore) {
                        *cluster = cpu->cores[i].l3_id;
                        return PQOS_RETVAL_OK;
                }

        return PQOS_RETVAL_ERROR;
}

int
pqos_cap_get_type(const struct pqos_cap *cap,
                  const enum pqos_cap_type type,
                  const struct pqos_capability **cap_item)
{
        int ret = PQOS_RETVAL_RESOURCE;
        unsigned i;

        if (cap == NULL || cap_item == NULL)
                return PQOS_RETVAL_PARAM;
        if (type >= PQOS_CAP_TYPE_NUMOF)
                return PQOS_RETVAL_PARAM;

        *cap_item = NULL;
        for (i = 0; i < cap->num_cap; i++) {
                if (cap->capabilities[i].type != type)
                        continue;
                *cap_item = &cap->capabilities[i];
                ret = PQOS_RETVAL_OK;
                break;
        }

        return ret;
}

const struct pqos_capability *
_pqos_cap_get_type(const enum pqos_cap_type type)
{
        const struct pqos_capability *cap_item;
        const struct pqos_cap *cap = _pqos_get_cap();
        int ret;

        ASSERT(cap != NULL);

        ret = pqos_cap_get_type(cap, type, &cap_item);
        if (ret == PQOS_RETVAL_OK)
                return cap_item;

        return NULL;
}

int
pqos_cap_get_event(const struct pqos_cap *cap,
                   const enum pqos_mon_event event,
                   const struct pqos_monitor **p_mon)
{
        const struct pqos_capability *cap_item = NULL;
        const struct pqos_cap_mon *mon = NULL;
        int ret = PQOS_RETVAL_OK;
        unsigned i;

        if (cap == NULL || p_mon == NULL)
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_MON, &cap_item);
        if (ret != PQOS_RETVAL_OK)
                return ret;

        ASSERT(cap_item != NULL);
        mon = cap_item->u.mon;

        ret = PQOS_RETVAL_ERROR;

        for (i = 0; i < mon->num_events; i++) {
                if (mon->events[i].type != event)
                        continue;

                *p_mon = &mon->events[i];
                ret = PQOS_RETVAL_OK;
                break;
        }

        return ret;
}

int
pqos_l3ca_get_cos_num(const struct pqos_cap *cap, unsigned *cos_num)
{
        const struct pqos_capability *item = NULL;
        int ret = PQOS_RETVAL_OK;

        ASSERT(cap != NULL && cos_num != NULL);
        if (cap == NULL || cos_num == NULL)
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_L3CA, &item);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no L3CA capability */

        ASSERT(item != NULL);
        *cos_num = item->u.l3ca->num_classes;
        return ret;
}

int
pqos_l2ca_get_cos_num(const struct pqos_cap *cap, unsigned *cos_num)
{
        const struct pqos_capability *item = NULL;
        int ret = PQOS_RETVAL_OK;

        ASSERT(cap != NULL && cos_num != NULL);
        if (cap == NULL || cos_num == NULL)
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_L2CA, &item);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no L2CA capability */

        ASSERT(item != NULL);
        *cos_num = item->u.l2ca->num_classes;
        return ret;
}

int
pqos_mba_get_cos_num(const struct pqos_cap *cap, unsigned *cos_num)
{
        const struct pqos_capability *item = NULL;
        int ret = PQOS_RETVAL_OK;

        ASSERT(cap != NULL && cos_num != NULL);
        if (cap == NULL || cos_num == NULL)
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_MBA, &item);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no MBA capability */

        ASSERT(item != NULL);
        *cos_num = item->u.mba->num_classes;
        return ret;
}

int
pqos_smba_get_cos_num(const struct pqos_cap *cap, unsigned *cos_num)
{
        const struct pqos_capability *item = NULL;
        int ret = PQOS_RETVAL_OK;

        ASSERT(cap != NULL && cos_num != NULL);
        if (cap == NULL || cos_num == NULL)
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_SMBA, &item);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no MBA capability */

        ASSERT(item != NULL);
        *cos_num = item->u.smba->num_classes;
        return ret;
}

int
pqos_l3ca_cdp_enabled(const struct pqos_cap *cap,
                      int *cdp_supported,
                      int *cdp_enabled)
{
        const struct pqos_capability *item = NULL;
        int ret = PQOS_RETVAL_OK;

        ASSERT(cap != NULL && (cdp_enabled != NULL || cdp_supported != NULL));
        if (cap == NULL || (cdp_enabled == NULL && cdp_supported == NULL))
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_L3CA, &item);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no L3CA capability */

        ASSERT(item != NULL);
        if (cdp_supported != NULL)
                *cdp_supported = item->u.l3ca->cdp;

        if (cdp_enabled != NULL)
                *cdp_enabled = item->u.l3ca->cdp_on;

        return ret;
}

int
pqos_l3ca_iordt_enabled(const struct pqos_cap *cap,
                        int *supported,
                        int *enabled)
{
        const struct pqos_capability *item = NULL;
        int ret = PQOS_RETVAL_OK;

        if (cap == NULL || (supported == NULL && enabled == NULL))
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_L3CA, &item);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no L3CA capability */

        if (supported != NULL)
                *supported = item->u.l3ca->iordt;

        if (enabled != NULL)
                *enabled = item->u.l3ca->iordt_on;

        return ret;
}

int
pqos_l2ca_cdp_enabled(const struct pqos_cap *cap,
                      int *cdp_supported,
                      int *cdp_enabled)
{
        const struct pqos_capability *l2ca = NULL;
        int ret = PQOS_RETVAL_OK;

        ASSERT(cap != NULL && (cdp_enabled != NULL || cdp_supported != NULL));
        if (cap == NULL || (cdp_enabled == NULL && cdp_supported == NULL))
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_L2CA, &l2ca);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no L2CA capability */

        ASSERT(l2ca != NULL);
        if (cdp_supported != NULL)
                *cdp_supported = l2ca->u.l2ca->cdp;

        if (cdp_enabled != NULL)
                *cdp_enabled = l2ca->u.l2ca->cdp_on;

        return ret;
}

int
pqos_mba_ctrl_enabled(const struct pqos_cap *cap,
                      int *ctrl_supported,
                      int *ctrl_enabled)
{
        const struct pqos_capability *mba_cap = NULL;
        int ret = PQOS_RETVAL_OK;

        ASSERT(cap != NULL && (ctrl_supported != NULL || ctrl_enabled != NULL));
        if (cap == NULL || (ctrl_supported == NULL && ctrl_enabled == NULL))
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_MBA, &mba_cap);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no MBA capability */

        ASSERT(mba_cap != NULL);
        if (ctrl_supported != NULL)
                *ctrl_supported = mba_cap->u.mba->ctrl;

        if (ctrl_enabled != NULL)
                *ctrl_enabled = mba_cap->u.mba->ctrl_on;

        return PQOS_RETVAL_OK;
}

int
pqos_mon_iordt_enabled(const struct pqos_cap *cap, int *supported, int *enabled)
{
        const struct pqos_capability *item = NULL;
        int ret = PQOS_RETVAL_OK;

        if (cap == NULL || (supported == NULL && enabled == NULL))
                return PQOS_RETVAL_PARAM;

        ret = pqos_cap_get_type(cap, PQOS_CAP_TYPE_MON, &item);
        if (ret != PQOS_RETVAL_OK)
                return ret; /**< no L3CA capability */

        if (supported != NULL)
                *supported = item->u.mon->iordt;

        if (enabled != NULL)
                *enabled = item->u.mon->iordt_on;

        return ret;
}

enum pqos_vendor
pqos_get_vendor(const struct pqos_cpuinfo *cpu)
{
        return cpu->vendor;
}

void
pqos_free(void *ptr)
{
        free(ptr);
}

#include "log.h"

pqos_channel_t
pqos_devinfo_get_channel_id(const struct pqos_devinfo *devinfo,
                            const uint16_t segment,
                            const uint16_t bdf,
                            const unsigned vc)
{
        size_t i;

        if (!devinfo || !devinfo->devs || vc >= PQOS_DEV_MAX_CHANNELS)
                return 0;

        for (i = 0; i < devinfo->num_devs; i++) {
                const struct pqos_dev *dev = &devinfo->devs[i];

                if (dev->segment != segment)
                        continue;

                if (dev->bdf != bdf)
                        continue;

                return dev->channel[vc];
        }

        return 0;
}

pqos_channel_t *
pqos_devinfo_get_channel_ids(const struct pqos_devinfo *devinfo,
                             const uint16_t segment,
                             const uint16_t bdf,
                             unsigned *num_channels)
{
        size_t i;

        if (!devinfo || !devinfo->devs || !num_channels)
                return NULL;

        for (i = 0; i < devinfo->num_devs; i++) {
                const struct pqos_dev *dev = &devinfo->devs[i];

                if (dev->segment != segment)
                        continue;

                if (dev->bdf != bdf)
                        continue;

                pqos_channel_t *channels = NULL;
                unsigned num_chan;
                size_t j;

                for (j = 0, num_chan = 0; j < devinfo->num_channels; j++) {
                        if (!dev->channel[j])
                                continue;
                        num_chan++;

                        pqos_channel_t *channels_tmp = realloc(
                            channels, sizeof(pqos_channel_t) * num_chan);

                        if (!channels_tmp) {
                                free(channels);
                                return NULL;
                        }
                        channels = channels_tmp;
                        channels[num_chan - 1] = dev->channel[j];
                }

                *num_channels = num_chan;
                return channels;
        }

        return NULL;
}

const struct pqos_channel *
pqos_devinfo_get_channel(const struct pqos_devinfo *dev,
                         const pqos_channel_t channel_id)
{
        unsigned i;

        if (dev == NULL || channel_id == 0 || dev->num_channels == 0 ||
            dev->channels == NULL)
                return NULL;

        for (i = 0; i < dev->num_channels; i++)
                if (dev->channels[i].channel_id == channel_id)
                        return &dev->channels[i];

        return NULL;
}

int
pqos_devinfo_get_channel_shared(const struct pqos_devinfo *dev,
                                const pqos_channel_t channel_id,
                                int *shared)
{
        unsigned d;
        unsigned count = 0;

        ASSERT(shared != NULL);

        if (dev == NULL)
                return PQOS_RETVAL_PARAM;

        for (d = 0; d < dev->num_devs; d++) {
                const struct pqos_dev *device = &dev->devs[d];
                unsigned c;

                for (c = 0; c < PQOS_DEV_MAX_CHANNELS; c++)
                        if (device->channel[c] == channel_id) {
                                ++count;

                                if (count > 1) {
                                        *shared = 1;
                                        return PQOS_RETVAL_OK;
                                }
                        }
        }

        if (count == 1) {
                *shared = 0;
                return PQOS_RETVAL_OK;
        }

        return PQOS_RETVAL_PARAM;
}
