/*
 * BSD LICENSE
 *
 * Copyright(c) 2017-2018 Intel Corporation. All rights reserved.
 * 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.O
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>             /**< scandir() */

#include "pqos.h"
#include "os_allocation.h"
#include "cap.h"
#include "log.h"
#include "types.h"
#include "resctrl.h"
#include "resctrl_alloc.h"
#include "resctrl_monitoring.h"

/**
 * ---------------------------------------
 * Local data structures
 * ---------------------------------------
 */
static const struct pqos_cap *m_cap = NULL;
static const struct pqos_cpuinfo *m_cpu = NULL;

/**
 * @brief Function to mount the resctrl file system with CDP option
 *
 * @param l3_cdp_cfg L3 CDP option
 * @param l2_cdp_cfg L2 CDP option
 * @return Operational status
 * @retval PQOS_RETVAL_OK on success
 */
static int
os_interface_mount(const enum pqos_cdp_config l3_cdp_cfg,
                   const enum pqos_cdp_config l2_cdp_cfg)
{
        const struct pqos_cap_l3ca *l3_cap = NULL;
        const struct pqos_cap_l2ca *l2_cap = NULL;
        const struct pqos_capability *alloc_cap = NULL;

        if ((l3_cdp_cfg != PQOS_REQUIRE_CDP_ON &&
            l3_cdp_cfg != PQOS_REQUIRE_CDP_OFF) || (
            l2_cdp_cfg != PQOS_REQUIRE_CDP_ON &&
            l2_cdp_cfg != PQOS_REQUIRE_CDP_OFF)) {
                LOG_ERROR("Invalid CDP mounting setting %d!\n",
                          l3_cdp_cfg);
                return PQOS_RETVAL_PARAM;
        }

        if (l3_cdp_cfg == PQOS_REQUIRE_CDP_OFF &&
                l2_cdp_cfg == PQOS_REQUIRE_CDP_OFF)
                goto mount;

        /* Get L3 CAT capabilities */
        (void) pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L3CA, &alloc_cap);
        if (alloc_cap != NULL)
                l3_cap = alloc_cap->u.l3ca;

        /* Get L2 CAT capabilities */
        (void) pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L2CA, &alloc_cap);
        if (alloc_cap != NULL)
                l2_cap = alloc_cap->u.l2ca;

        if (l3_cap != NULL && !l3_cap->os_cdp &&
                l3_cdp_cfg == PQOS_REQUIRE_CDP_ON) {
                /* Check against erroneous CDP request */
                LOG_ERROR("L3 CDP requested but not supported by the "
                          "platform!\n");
                return PQOS_RETVAL_PARAM;
        }
        if (l2_cap != NULL && !l2_cap->os_cdp &&
                l2_cdp_cfg == PQOS_REQUIRE_CDP_ON) {
                /* Check against erroneous CDP request */
                LOG_ERROR("L2 CDP requested but not supported by the "
                          "platform!\n");
                return PQOS_RETVAL_PARAM;
        }

 mount:
        return resctrl_mount(l3_cdp_cfg, l2_cdp_cfg);
}

/**
 * @brief Check to see if resctrl is supported.
 *        If it is attempt to mount the file system.
 *
 * @return Operational status
 */
static int
os_alloc_check(void)
{
        int ret;
        unsigned i, supported = 0;

        /**
         * Check if resctrl is supported
         */
        for (i = 0; i < m_cap->num_cap; i++) {
                if (m_cap->capabilities[i].os_support == 1) {
                        if (m_cap->capabilities[i].type == PQOS_CAP_TYPE_L3CA)
                                supported = 1;
                        if (m_cap->capabilities[i].type == PQOS_CAP_TYPE_L2CA)
                                supported = 1;
                        if (m_cap->capabilities[i].type == PQOS_CAP_TYPE_MBA)
                                supported = 1;
                }
        }

        if (!supported)
                return PQOS_RETVAL_OK;
        /**
         * Check if resctrl is mounted
         */
        if (access(RESCTRL_PATH"/cpus", F_OK) != 0) {
                const struct pqos_capability *alloc_cap = NULL;
                enum pqos_cdp_config l3_cdp_mount = PQOS_REQUIRE_CDP_OFF;
                enum pqos_cdp_config l2_cdp_mount = PQOS_REQUIRE_CDP_OFF;

                /* Get L3 CAT capabilities */
                (void) pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L3CA, &alloc_cap);
                if (alloc_cap != NULL && alloc_cap->u.l3ca->cdp_on)
                        l3_cdp_mount = PQOS_REQUIRE_CDP_ON;

                /* Get L2 CAT capabilities */
                (void) pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L2CA, &alloc_cap);
                if (alloc_cap != NULL && alloc_cap->u.l2ca->cdp_on)
                        l2_cdp_mount = PQOS_REQUIRE_CDP_ON;

                ret = os_interface_mount(l3_cdp_mount, l2_cdp_mount);
                if (ret != PQOS_RETVAL_OK) {
                        LOG_INFO("Unable to mount resctrl\n");
                        return PQOS_RETVAL_RESOURCE;
                }
        }

        return PQOS_RETVAL_OK;
}

/**
 * @brief Prepares and authenticates resctrl file system
 *        used for OS allocation interface
 *
 * @return Operational status
 * @retval PQOS_RETVAL_OK success
 */
static int
os_alloc_prep(void)
{
        unsigned i, num_grps = 0;
        int ret;

        ASSERT(m_cap != NULL);
        ret = resctrl_alloc_get_grps_num(m_cap, &num_grps);
	if (ret != PQOS_RETVAL_OK)
		return ret;
        /*
         * Detect/Create all available COS resctrl groups
         */
	for (i = 1; i < num_grps; i++) {
		char buf[128];
		struct stat st;

		memset(buf, 0, sizeof(buf));
		if (snprintf(buf, sizeof(buf) - 1,
                             "%s/COS%d", RESCTRL_PATH, (int) i) < 0)
			return PQOS_RETVAL_ERROR;

		/* if resctrl group doesn't exist - create it */
		if (stat(buf, &st) == 0) {
			LOG_DEBUG("resctrl group COS%d detected\n", i);
			continue;
		}

		if (mkdir(buf, 0755) == -1) {
			LOG_DEBUG("Failed to create resctrl group %s!\n", buf);
			return PQOS_RETVAL_BUSY;
		}
		LOG_DEBUG("resctrl group COS%d created\n", i);
	}

	return PQOS_RETVAL_OK;
}

int
os_alloc_init(const struct pqos_cpuinfo *cpu, const struct pqos_cap *cap)
{
	int ret;

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

	m_cap = cap;
	m_cpu = cpu;

        ret = os_alloc_check();
        if (ret != PQOS_RETVAL_OK)
                return ret;

        ret = resctrl_alloc_init(cpu, cap);
        if (ret != PQOS_RETVAL_OK)
                return ret;

        ret = os_alloc_prep();

        return ret;
}

int
os_alloc_fini(void)
{
        int ret = PQOS_RETVAL_OK;

        m_cap = NULL;
        m_cpu = NULL;
        return ret;
}

int
os_alloc_assoc_set(const unsigned lcore, const unsigned class_id)
{
	int ret;
	unsigned grps;
        int ret_mon;
        char mon_group[256];

        ASSERT(m_cpu != NULL);
        ASSERT(m_cap != NULL);

        ret = pqos_cpu_check_core(m_cpu, lcore);
        if (ret != PQOS_RETVAL_OK)
                return PQOS_RETVAL_PARAM;

        ret = resctrl_alloc_get_grps_num(m_cap, &grps);
        if (ret != PQOS_RETVAL_OK)
                return ret;

	if (class_id >= grps)
		/* class_id is out of bounds */
		return PQOS_RETVAL_PARAM;

        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                return ret;

        /*
         * When core is moved to different COS we need to update monitoring
         * groups. Obtain monitoring group name
         */
        ret_mon = resctrl_mon_assoc_get(lcore, mon_group, sizeof(mon_group));
        if (ret_mon != PQOS_RETVAL_OK && ret_mon != PQOS_RETVAL_RESOURCE)
                LOG_WARN("Failed to obtain monitoring group assigment for "
                         "core %u\n", lcore);

        ret = resctrl_alloc_assoc_set(lcore, class_id);
        if (ret != PQOS_RETVAL_OK)
                goto os_alloc_assoc_set_exit;

        /* Core monitoring was started assign it back to monitoring group */
        if (ret_mon == PQOS_RETVAL_OK) {
                ret_mon = resctrl_mon_assoc_set(lcore, mon_group);
                if (ret_mon != PQOS_RETVAL_OK)
                        LOG_WARN("Could not assign core %d back to monitoring "
                                 "group\n", lcore);
        }

 os_alloc_assoc_set_exit:
        resctrl_lock_release();

	return ret;
}

int
os_alloc_assoc_get(const unsigned lcore, unsigned *class_id)
{
        int ret;

        ASSERT(class_id != NULL);
        ASSERT(m_cpu != NULL);

        ret = pqos_cpu_check_core(m_cpu, lcore);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_PARAM;

        ret = resctrl_lock_shared();
        if (ret != PQOS_RETVAL_OK)
                return ret;

        ret = resctrl_alloc_assoc_get(lcore, class_id);

        resctrl_lock_release();

	return ret;
}

unsigned *
os_pid_get_pid_assoc(const unsigned class_id, unsigned *count)
{
        unsigned grps;
        int ret;
        unsigned *tasks;

	ASSERT(m_cap != NULL);
        ASSERT(count != NULL);
        ret = resctrl_alloc_get_grps_num(m_cap, &grps);
	if (ret != PQOS_RETVAL_OK)
		return NULL;

	if (class_id >= grps)
		/* class_id is out of bounds */
		return NULL;

        ret = resctrl_lock_shared();
        if (ret != PQOS_RETVAL_OK)
                return NULL;

	tasks = resctrl_alloc_task_read(class_id, count);

        resctrl_lock_release();

        return tasks;
}

/**
 * @brief Gets unused COS
 *
 * The lowest acceptable COS is 1, as 0 is a default one
 *
 * @param [in] hi_class_id highest acceptable COS id
 * @param [out] class_id unused COS
 *
 * @return Operation status
 */
static int
get_unused_cos(const unsigned hi_class_id,
               unsigned *class_id)
{
        unsigned used_classes[hi_class_id + 1];
        unsigned i, cos;
        int ret;

        if (class_id == NULL)
                return PQOS_RETVAL_PARAM;

        memset(used_classes, 0, sizeof(used_classes));

        for (i = hi_class_id; i != 0; i--) {
                struct resctrl_cpumask mask;
                unsigned j;

                ret = resctrl_alloc_cpumask_read(i, &mask);
                if (ret != PQOS_RETVAL_OK)
			return ret;

                for (j = 0; j < sizeof(mask.tab); j++)
                        if (mask.tab[j] > 0) {
                                used_classes[i] = 1;
                                break;
                        }

                if (used_classes[i] == 1)
                        continue;

                ret = resctrl_alloc_task_file_check(i, &used_classes[i]);
                if (ret != PQOS_RETVAL_OK)
			return ret;
        }

        /* Find unused COS */
        for (cos = hi_class_id; cos != 0; cos--) {
                if (used_classes[cos] == 0) {
                        *class_id = cos;
                        return PQOS_RETVAL_OK;
                }
        }

        return PQOS_RETVAL_RESOURCE;
}

int
os_alloc_assign(const unsigned technology,
                const unsigned *core_array,
                const unsigned core_num,
                unsigned *class_id)
{
        unsigned i, num_rctl_grps = 0;
        int ret;

        ASSERT(core_num > 0);
        ASSERT(core_array != NULL);
        ASSERT(class_id != NULL);
        ASSERT(m_cap != NULL);
        UNUSED_PARAM(technology);

        /* obtain highest class id for all requested technologies */
        ret = resctrl_alloc_get_grps_num(m_cap, &num_rctl_grps);
        if (ret != PQOS_RETVAL_OK)
                return ret;

        if (num_rctl_grps == 0)
                return PQOS_RETVAL_ERROR;

        /* find an unused class from highest down */
        ret = get_unused_cos(num_rctl_grps - 1, class_id);
        if (ret != PQOS_RETVAL_OK)
                return ret;

        /* assign cores to the unused class */
        for (i = 0; i < core_num; i++) {
                ret = os_alloc_assoc_set(core_array[i], *class_id);
                if (ret != PQOS_RETVAL_OK)
                        return ret;
        }

        return ret;
}

int
os_alloc_release(const unsigned *core_array, const unsigned core_num)
{
        int ret;
        unsigned i, cos0 = 0;
        struct resctrl_cpumask mask;

	ASSERT(m_cpu != NULL);
        ASSERT(core_num > 0 && core_array != NULL);

        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                return ret;

        /**
         * Set the CPU assoc back to COS0
         */
        ret = resctrl_alloc_cpumask_read(cos0, &mask);
        if (ret != PQOS_RETVAL_OK)
                goto os_alloc_release_unlock;
        for (i = 0; i < core_num; i++) {
		if (core_array[i] >= m_cpu->num_cores) {
			ret = PQOS_RETVAL_ERROR;
                        goto os_alloc_release_unlock;
                }
                resctrl_cpumask_set(core_array[i], &mask);
	}

        ret = resctrl_alloc_cpumask_write(cos0, &mask);
        if (ret != PQOS_RETVAL_OK)
                LOG_ERROR("CPU assoc reset failed\n");

 os_alloc_release_unlock:
        resctrl_lock_release();

        return ret;
}

/**
 * @brief Moves all cores to COS0 (default)
 *
 * @return Operation status
 */
static int
os_alloc_reset_cores(void)
{
	const unsigned default_cos = 0;
	struct resctrl_cpumask mask;
	unsigned i;
	int ret;

	LOG_INFO("OS alloc reset - core assoc\n");

	ret = resctrl_alloc_cpumask_read(default_cos, &mask);
	if (ret != PQOS_RETVAL_OK)
		return ret;

	for (i = 0; i < m_cpu->num_cores; i++)
		resctrl_cpumask_set(m_cpu->cores[i].lcore, &mask);

	ret = resctrl_alloc_cpumask_write(default_cos, &mask);
	if (ret != PQOS_RETVAL_OK)
		LOG_ERROR("Core assoc reset failed\n");

	return ret;
}

/**
 * @brief Resets L3, L2 and MBA schematas to default value
 *
 * @param [in] l3_cap l3 cache capability
 * @param [in] l2_cap l2 cache capability
 *
 * @return Operation status
 */
static int
os_alloc_reset_schematas(const struct pqos_cap_l3ca *l3_cap,
		     const struct pqos_cap_l2ca *l2_cap){

	const unsigned default_mba = 100;
	uint64_t default_l3ca = 0;
	uint64_t default_l2ca = 0;
	unsigned grps;
	unsigned i, j;
	int ret;

	LOG_INFO("OS alloc reset - schematas\n");

	ret = resctrl_lock_exclusive();
	if (ret != PQOS_RETVAL_OK)
		return ret;

	ret = resctrl_alloc_get_grps_num(m_cap, &grps);
	if (ret != PQOS_RETVAL_OK)
		goto os_alloc_reset_light_schematas_exit;

	/**
	 * Obtain default L2/L3 CAT mask
	 */
	if (l3_cap != NULL)
		default_l3ca = ((uint64_t)1 << l3_cap->num_ways) - 1;

	if (l2_cap != NULL)
		default_l2ca = ((uint64_t)1 << l2_cap->num_ways) - 1;

	/**
	 * Reset schematas
	 */
	for (i = 0; i < grps; i++) {
		struct resctrl_alloc_schemata schmt;

		ret = resctrl_alloc_schemata_init(i, m_cap, m_cpu, &schmt);
		if (ret != PQOS_RETVAL_OK) {
			LOG_ERROR("Error on schemata init "
				  "for resctrl group %u\n", i);
			goto os_alloc_reset_light_schematas_exit;
		}

		for (j = 0; j < schmt.l3ca_num; j++) {
			 /* l3ca_num should be greater
			  * than 0 if l3_cap is provided */
			ASSERT(l3_cap != NULL);
			if (schmt.l3ca[j].cdp) {
				schmt.l3ca[j].u.s.code_mask = default_l3ca;
				schmt.l3ca[j].u.s.data_mask = default_l3ca;
			} else
				schmt.l3ca[j].u.ways_mask = default_l3ca;
		}

		for (j = 0; j < schmt.l2ca_num; j++) {
			/* l2ca_num should be greater
			 * than 0 if l2_cap is provided */
			ASSERT(l2_cap != NULL);
			if (schmt.l2ca[j].cdp) {
				schmt.l2ca[j].u.s.code_mask = default_l2ca;
				schmt.l2ca[j].u.s.data_mask = default_l2ca;
			} else
				schmt.l2ca[j].u.ways_mask = default_l2ca;
		}

		for (j = 0; j < schmt.mba_num; j++)
			schmt.mba[j].mb_rate = default_mba;

		ret = resctrl_alloc_schemata_write(i, &schmt);

		resctrl_alloc_schemata_fini(&schmt);

		if (ret != PQOS_RETVAL_OK) {
			LOG_ERROR("Error on schemata write "
				  "for resctrl group %u\n", i);
			goto os_alloc_reset_light_schematas_exit;
		}
	}

 os_alloc_reset_light_schematas_exit:
	if (ret != PQOS_RETVAL_OK)
		resctrl_lock_release();
	else
		ret = resctrl_lock_release();

	return ret;
}

/**
 * @brief filter for scandir.
 *
 * Skips entries starting with "."
 * Skips non-numeric entries
 *
 * @param[in] dir scandir dirent entry
 *
 * @retval 0 for entires to be skipped
 * @retval 1 otherwise
 */
static int
filter_pids(const struct dirent *dir)
{
	size_t char_idx;

	if (dir->d_name[0] == '.')
		return 0;

        for (char_idx = 0; char_idx < strlen(dir->d_name); ++char_idx) {
		if (!isdigit(dir->d_name[char_idx]))
			return 0;
        }

        return 1;
}

/**
 * @brief Move all tasks to COS0 (default)
 *
 * @return Operation status
 */
static int
os_alloc_reset_tasks(void)
{
	struct dirent **pids_list = NULL;
	int pid_count;
	int pid_idx;
	int alloc_result;
	pid_t pid;
	const int cos0 = 0;

	LOG_INFO("OS alloc reset - tasks\n");

	pid_count = scandir("/proc", &pids_list, filter_pids, NULL);

	for (pid_idx = 0; pid_idx < pid_count; ++pid_idx) {
		pid = atoi(pids_list[pid_idx]->d_name);
		alloc_result = os_alloc_assoc_set_pid(pid, cos0);
		if (alloc_result != PQOS_RETVAL_OK) {
                        int result;

                        result = resctrl_alloc_task_validate(pid);
                        if (result != PQOS_RETVAL_OK) {
                                LOG_DEBUG("Task %d no longer exists\n", pid);
                                alloc_result = PQOS_RETVAL_OK;
                                continue;
                        }

			LOG_ERROR("Error allocating task %d to COS%u\n",
				   pid, cos0);
			return alloc_result;
		}
	}

	return PQOS_RETVAL_OK;
}

/**
 * @brief Performs "light reset" of CAT.
 *
 * Moves all cores to default COS
 * Resets all l3 schematas to default value
 * Resets all l2 schematas to default value
 * Resets all mba schematas to default value
 * Moves all tasks to default COS
 *
 * @param [in] l3_cap id of default COS
 * @param [in] l2_cap id of default COS
 *
 * @return Operation status
 */
static int
os_alloc_reset_light(const struct pqos_cap_l3ca *l3_cap,
		     const struct pqos_cap_l2ca *l2_cap)
{
	int ret = PQOS_RETVAL_OK;
	int step_result;

	LOG_INFO("OS alloc reset - LIGHT\n");

	step_result = os_alloc_reset_cores();
	if (step_result != PQOS_RETVAL_OK)
		ret = step_result;

	step_result = os_alloc_reset_schematas(l3_cap, l2_cap);
	if (step_result != PQOS_RETVAL_OK)
		ret = step_result;

	step_result = os_alloc_reset_tasks();
	if (step_result != PQOS_RETVAL_OK)
		ret = step_result;

	return ret;
}

/**
 * @brief updates CDP for l2 cache capability
 *
 * @param [in] l2_cdp_cfg requsted l2 cdp configuration
 * @param [in] l2_cap l2 capability
 * @param [out] l2_cdp id of default COS
 *
 * @retval > 0 on l2_cdp change
 * @retval 0 on no change
 */
static int
l2_cdp_update(const enum pqos_cdp_config l2_cdp_cfg,
	      const struct pqos_cap_l2ca *l2_cap,
	      enum pqos_cdp_config *l2_cdp)
{
        int ret = 0;

	if (l2_cap == NULL)
		return 0;
	switch (l2_cdp_cfg) {
	case PQOS_REQUIRE_CDP_ON:
		*l2_cdp = l2_cdp_cfg;
                ret = !l2_cap->cdp_on;
		break;
	case PQOS_REQUIRE_CDP_ANY:
                if (l2_cap->cdp_on)
                        *l2_cdp = PQOS_REQUIRE_CDP_ON;
                else
                        *l2_cdp = PQOS_REQUIRE_CDP_OFF;
		break;
	case PQOS_REQUIRE_CDP_OFF:
		*l2_cdp = l2_cdp_cfg;
                ret = l2_cap->cdp_on;
		break;
	}

	return ret;
}

/**
 * @brief updates CDP for l3 cache capability
 *
 * @param [in] l3_cdp_cfg requsted l3 cdp configuration
 * @param [in] l3_cap l3 capability
 * @param [out] l3_cdp id of default COS
 *
 * @retval > 0 on l3_cdp change
 * @retval 0 on no change
 */
static int
l3_cdp_update(const enum pqos_cdp_config l3_cdp_cfg,
	      const struct pqos_cap_l3ca *l3_cap,
	      enum pqos_cdp_config *l3_cdp)
{
        int ret = 0;

	if (l3_cap == NULL)
		return 0;

	switch (l3_cdp_cfg) {
	case PQOS_REQUIRE_CDP_ON:
		*l3_cdp = l3_cdp_cfg;
                ret = !l3_cap->cdp_on;
		break;
	case PQOS_REQUIRE_CDP_ANY:
                if (l3_cap->cdp_on)
                        *l3_cdp = PQOS_REQUIRE_CDP_ON;
                else
                        *l3_cdp = PQOS_REQUIRE_CDP_OFF;
		break;
	case PQOS_REQUIRE_CDP_OFF:
		*l3_cdp = l3_cdp_cfg;
                ret = l3_cap->cdp_on;
		break;
	}

	return ret;
}

/**
 * @brief Performs "full reset" of CAT.
 *
 * Allocs all cores to default COS
 * Remounts resctrl file system with new CDP configuration
 *
 * @param [in] l3_cdp_cfg requested L3 CAT CDP config
 * @param [in] l2_cdp_cfg requested L2 CAT CDP config
 * @param [in] l3_cap l3 capability
 * @param [in] l2_cap l2 capability
 *
 * @return Operation status
 */
static int
os_alloc_reset_full(const enum pqos_cdp_config l3_cdp_cfg,
		    const enum pqos_cdp_config l2_cdp_cfg,
	            const struct pqos_cap_l3ca *l3_cap,
	            const struct pqos_cap_l2ca *l2_cap)
{
	int ret;

        LOG_INFO("OS alloc reset - FULL\n");

        /**
         * Set the CPU assoc back to COS0
         */
        ret = os_alloc_reset_cores();
	if (ret != PQOS_RETVAL_OK)
		goto os_alloc_reset_full_exit;

        /**
         * Umount resctrl to reset schemata
         */

	LOG_INFO("OS alloc reset - unmount resctrl\n");
        ret = resctrl_umount();
        if (ret != PQOS_RETVAL_OK)
                goto os_alloc_reset_full_exit;

        /**
         * Mount now with CDP option.
         */
	LOG_INFO("OS alloc reset - mount resctrl with "
		"l3_cdp %s and l2_cdp %s\n",
		l3_cdp_cfg ? "on" : "off",
		l2_cdp_cfg ? "on" : "off");

        ret = os_interface_mount(l3_cdp_cfg, l2_cdp_cfg);
        if (ret != PQOS_RETVAL_OK) {
                LOG_ERROR("Mount OS interface error!\n");
                goto os_alloc_reset_full_exit;
        }
        if (l3_cap != NULL)
                _pqos_cap_l3cdp_change(l3_cdp_cfg);
        if (l2_cap != NULL)
                _pqos_cap_l2cdp_change(l2_cdp_cfg);
        /**
         * Create the COS dir's in resctrl.
         */
        LOG_INFO("OS alloc reset - prepare resctrl fs\n");
        ret = os_alloc_prep();
        if (ret != PQOS_RETVAL_OK)
                LOG_ERROR("OS alloc prep error!\n");

 os_alloc_reset_full_exit:
        return ret;
}

int
os_alloc_reset(const enum pqos_cdp_config l3_cdp_cfg,
               const enum pqos_cdp_config l2_cdp_cfg)
{
        const struct pqos_capability *alloc_cap = NULL;
        const struct pqos_cap_l3ca *l3_cap = NULL;
        const struct pqos_cap_l2ca *l2_cap = NULL;
        enum pqos_cdp_config l3_cdp = PQOS_REQUIRE_CDP_OFF;
	enum pqos_cdp_config l2_cdp = PQOS_REQUIRE_CDP_OFF;
	int l3_cdp_changed = 0;
	int l2_cdp_changed = 0;
        int ret;

        ASSERT(l3_cdp_cfg == PQOS_REQUIRE_CDP_ON ||
               l3_cdp_cfg == PQOS_REQUIRE_CDP_OFF ||
               l3_cdp_cfg == PQOS_REQUIRE_CDP_ANY);

        ASSERT(l2_cdp_cfg == PQOS_REQUIRE_CDP_ON ||
               l2_cdp_cfg == PQOS_REQUIRE_CDP_OFF ||
               l2_cdp_cfg == PQOS_REQUIRE_CDP_ANY);

        /* Get L3 CAT capabilities */
        (void) pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L3CA, &alloc_cap);
        if (alloc_cap != NULL)
                l3_cap = alloc_cap->u.l3ca;

        /* Get L2 CAT capabilities */
        alloc_cap = NULL;
        (void) pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L2CA, &alloc_cap);
        if (alloc_cap != NULL)
                l2_cap = alloc_cap->u.l2ca;

        /* Check if either L2 CAT or L3 CAT is supported */
        if (l2_cap == NULL && l3_cap == NULL) {
		LOG_ERROR("L2 CAT/L3 CAT not present!\n");
		ret = PQOS_RETVAL_RESOURCE; /* no L2/L3 CAT present */
		goto os_alloc_reset_exit;
        }
        /* Check L3 CDP requested while not present */
        if (l3_cap == NULL && l3_cdp_cfg != PQOS_REQUIRE_CDP_ANY) {
		LOG_ERROR("L3 CDP setting requested but no L3 CAT present!\n");
		ret = PQOS_RETVAL_RESOURCE;
		goto os_alloc_reset_exit;
        }
        /* Check against erroneous CDP request */
        if (l3_cap != NULL && l3_cdp_cfg == PQOS_REQUIRE_CDP_ON &&
		!l3_cap->os_cdp) {
		LOG_ERROR("L3 CAT/CDP requested but not supported by the "
                          "platform!\n");
		ret = PQOS_RETVAL_PARAM;
		goto os_alloc_reset_exit;
        }
        /* Check L2 CDP requested while not present */
        if (l2_cap == NULL && l2_cdp_cfg != PQOS_REQUIRE_CDP_ANY) {
                LOG_ERROR("L2 CDP setting requested but no L2 CAT present!\n");
                ret = PQOS_RETVAL_RESOURCE;
                goto os_alloc_reset_exit;
        }
        /* Check against erroneous L2 CDP request */
        if (l2_cap != NULL && l2_cdp_cfg == PQOS_REQUIRE_CDP_ON &&
                !l2_cap->os_cdp) {
                LOG_ERROR("L2 CAT/CDP requested but not supported by the "
                          "platform!\n");
                ret = PQOS_RETVAL_PARAM;
                goto os_alloc_reset_exit;
        }

        /** Check if resctrl is in use */
        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                goto os_alloc_reset_exit;
        resctrl_lock_release();

        l3_cdp_changed = l3_cdp_update(l3_cdp_cfg, l3_cap, &l3_cdp);
        l2_cdp_changed = l2_cdp_update(l2_cdp_cfg, l2_cap, &l2_cdp);

        if (l3_cdp_changed || l2_cdp_changed) {

		unsigned monitoring_active = 0;

		ret = resctrl_mon_active(&monitoring_active);
		if (ret != PQOS_RETVAL_OK) {
			LOG_ERROR("Failed to check resctrl "
				  "monitoring activity\n");
			goto os_alloc_reset_exit;
		}

		if (monitoring_active > 0) {
			LOG_ERROR("Failed to reset allocation. Please stop "
				  "monitoring in order to change CDP settings\n");
			ret = PQOS_RETVAL_ERROR;
		} else
			ret = os_alloc_reset_full(l3_cdp, l2_cdp, l3_cap,
                                                  l2_cap);
        } else
		ret = os_alloc_reset_light(l3_cap, l2_cap);

 os_alloc_reset_exit:
        return ret;
}

int
os_l3ca_set(const unsigned socket,
            const unsigned num_cos,
            const struct pqos_l3ca *ca)
{
	int ret;
	unsigned sockets_num = 0;
	unsigned *sockets = NULL;
	unsigned i;
	unsigned num_grps = 0, l3ca_num;
	int cdp_enabled = 0;

	ASSERT(ca != NULL);
	ASSERT(num_cos != 0);
	ASSERT(m_cap != NULL);
	ASSERT(m_cpu != NULL);

	ret = pqos_l3ca_get_cos_num(m_cap, &l3ca_num);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_RESOURCE; /* L3 CAT not supported */

	ret = resctrl_alloc_get_grps_num(m_cap, &num_grps);
	if (ret != PQOS_RETVAL_OK)
		return ret;

	if (num_cos > num_grps)
		return PQOS_RETVAL_ERROR;

	/* Get number of sockets in the system */
	sockets = pqos_cpu_get_sockets(m_cpu, &sockets_num);
	if (sockets == NULL || sockets_num == 0) {
		ret = PQOS_RETVAL_ERROR;
		goto os_l3ca_set_exit;
	}

	if (socket >= sockets_num) {
		ret = PQOS_RETVAL_PARAM;
		goto os_l3ca_set_exit;
	}

	ret = pqos_l3ca_cdp_enabled(m_cap, NULL, &cdp_enabled);
	if (ret != PQOS_RETVAL_OK)
		goto os_l3ca_set_exit;

        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                goto os_l3ca_set_exit;

	for (i = 0; i < num_cos; i++) {
		struct resctrl_alloc_schemata schmt;

		if (ca[i].cdp == 1 && cdp_enabled == 0) {
			LOG_ERROR("Attempting to set CDP COS while L3 CDP "
			          "is disabled!\n");
			ret = PQOS_RETVAL_ERROR;
			goto os_l3ca_set_unlock;
		}

		ret = resctrl_alloc_schemata_init(ca[i].class_id, m_cap, m_cpu,
		                                  &schmt);

		/* read schemata file */
		if (ret == PQOS_RETVAL_OK)
			ret = resctrl_alloc_schemata_read(ca[i].class_id,
			                                  &schmt);

		/* update and write schemata */
		if (ret == PQOS_RETVAL_OK) {
			struct pqos_l3ca *l3ca = &(schmt.l3ca[socket]);

			if (cdp_enabled == 1 && ca[i].cdp == 0) {
				l3ca->cdp = 1;
				l3ca->u.s.data_mask = ca[i].u.ways_mask;
				l3ca->u.s.code_mask = ca[i].u.ways_mask;
			} else
				*l3ca = ca[i];

			ret = resctrl_alloc_schemata_write(ca[i].class_id,
				                           &schmt);
		}

		resctrl_alloc_schemata_fini(&schmt);

		if (ret != PQOS_RETVAL_OK)
			goto os_l3ca_set_unlock;
	}

 os_l3ca_set_unlock:
        resctrl_lock_release();

 os_l3ca_set_exit:
	if (sockets != NULL)
		free(sockets);

	return ret;
}

int
os_l3ca_get(const unsigned socket,
            const unsigned max_num_ca,
            unsigned *num_ca,
            struct pqos_l3ca *ca)
{
	int ret;
	unsigned class_id;
	unsigned count = 0;
	unsigned sockets_num = 0;
	unsigned *sockets = NULL;

	ASSERT(num_ca != NULL);
	ASSERT(ca != NULL);
	ASSERT(max_num_ca != 0);
	ASSERT(m_cap != NULL);
	ASSERT(m_cpu != NULL);

	ret = pqos_l3ca_get_cos_num(m_cap, &count);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_RESOURCE; /* L3 CAT not supported */

	ret = resctrl_alloc_get_grps_num(m_cap, &count);
	if (ret != PQOS_RETVAL_OK)
		return ret;

	if (count > max_num_ca)
		return PQOS_RETVAL_ERROR;

	sockets = pqos_cpu_get_sockets(m_cpu, &sockets_num);
	if (sockets == NULL || sockets_num == 0) {
		ret = PQOS_RETVAL_ERROR;
		goto os_l3ca_get_exit;
	}

	if (socket >= sockets_num) {
		ret = PQOS_RETVAL_PARAM;
		goto os_l3ca_get_exit;
	}

        ret = resctrl_lock_shared();
        if (ret != PQOS_RETVAL_OK)
                goto os_l3ca_get_exit;

	for (class_id = 0; class_id < count; class_id++) {
		struct resctrl_alloc_schemata schmt;

		ret = resctrl_alloc_schemata_init(class_id, m_cap, m_cpu,
		                                  &schmt);
		if (ret == PQOS_RETVAL_OK)
			ret = resctrl_alloc_schemata_read(class_id, &schmt);

		if (ret == PQOS_RETVAL_OK)
			ca[class_id] = schmt.l3ca[socket];

		resctrl_alloc_schemata_fini(&schmt);

		if (ret != PQOS_RETVAL_OK)
			goto os_l3ca_get_unlock;
	}
	*num_ca = count;

 os_l3ca_get_unlock:
        resctrl_lock_release();

 os_l3ca_get_exit:
	if (sockets != NULL)
		free(sockets);

	return ret;
}

int
os_l3ca_get_min_cbm_bits(unsigned *min_cbm_bits)
{
	int ret = PQOS_RETVAL_OK;
	char buf[128];
	const struct pqos_capability *l3_cap = NULL;
	FILE *fd;

	ASSERT(m_cap != NULL);
	ASSERT(min_cbm_bits != NULL);

	/**
	 * Get L3 CAT capabilities
	 */
	ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L3CA, &l3_cap);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_RESOURCE; /* L3 CAT not supported */

	memset(buf, 0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "%s/info/L3/min_cbm_bits", RESCTRL_PATH);

	fd = fopen(buf, "r");
	if (fd == NULL)
		return PQOS_RETVAL_ERROR;

	if (fscanf(fd, "%u", min_cbm_bits) != 1)
		ret = PQOS_RETVAL_ERROR;

	fclose(fd);

	return ret;
}

int
os_l2ca_set(const unsigned l2id,
            const unsigned num_cos,
            const struct pqos_l2ca *ca)
{
	int ret;
	unsigned i;
	unsigned l2ids_num = 0;
	unsigned *l2ids = NULL;
	unsigned num_grps = 0, l2ca_num;
        int cdp_enabled;

	ASSERT(m_cap != NULL);
	ASSERT(ca != NULL);
	ASSERT(num_cos != 0);

	ret = pqos_l2ca_get_cos_num(m_cap, &l2ca_num);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_RESOURCE; /* L2 CAT not supported */

	ret = resctrl_alloc_get_grps_num(m_cap, &num_grps);
	if (ret != PQOS_RETVAL_OK)
		return ret;

	if (num_cos > num_grps)
		return PQOS_RETVAL_PARAM;

        ret = pqos_l2ca_cdp_enabled(m_cap, NULL, &cdp_enabled);
        if (ret != PQOS_RETVAL_OK)
                goto os_l2ca_set_exit;

	/*
	 * Check if class id's are within allowed range.
	 */
	for (i = 0; i < num_cos; i++) {
		if (ca[i].class_id >= l2ca_num) {
			LOG_ERROR("L2 COS%u is out of range (COS%u is max)!\n",
			          ca[i].class_id, l2ca_num - 1);
			return PQOS_RETVAL_PARAM;
		}
	}

	/* Get number of L2 ids in the system */
	l2ids = pqos_cpu_get_l2ids(m_cpu, &l2ids_num);
	if (l2ids == NULL || l2ids_num == 0) {
		ret = PQOS_RETVAL_ERROR;
		goto os_l2ca_set_exit;
	}

	if (l2id >= l2ids_num) {
		ret = PQOS_RETVAL_PARAM;
		goto os_l2ca_set_exit;
	}

        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                goto os_l2ca_set_exit;

	for (i = 0; i < num_cos; i++) {
		struct resctrl_alloc_schemata schmt;

                if (ca[i].cdp == 1 && cdp_enabled == 0) {
                        LOG_ERROR("Attempting to set CDP COS while L2 CDP "
                                  "is disabled!\n");
                        ret = PQOS_RETVAL_ERROR;
                        goto os_l2ca_set_unlock;
                }

		ret = resctrl_alloc_schemata_init(ca[i].class_id, m_cap, m_cpu,
		                                  &schmt);

		/* read schemata file */
		if (ret == PQOS_RETVAL_OK)
			ret = resctrl_alloc_schemata_read(ca[i].class_id,
			                                  &schmt);

                /* update and write schemata */
                if (ret == PQOS_RETVAL_OK) {
                        struct pqos_l2ca *l2ca = &(schmt.l2ca[l2id]);

                        if (cdp_enabled == 1 && ca[i].cdp == 0) {
                                l2ca->cdp = 1;
                                l2ca->u.s.data_mask = ca[i].u.ways_mask;
                                l2ca->u.s.code_mask = ca[i].u.ways_mask;
                        } else
                                *l2ca = ca[i];

                        ret = resctrl_alloc_schemata_write(ca[i].class_id,
                                                           &schmt);
                }

		resctrl_alloc_schemata_fini(&schmt);

		if (ret != PQOS_RETVAL_OK)
			goto os_l2ca_set_unlock;
	}

 os_l2ca_set_unlock:
        resctrl_lock_release();

 os_l2ca_set_exit:
	if (l2ids != NULL)
		free(l2ids);

	return ret;
}

int
os_l2ca_get(const unsigned l2id,
            const unsigned max_num_ca,
            unsigned *num_ca,
            struct pqos_l2ca *ca)
{
	int ret;
	unsigned class_id;
	unsigned count = 0;
	unsigned l2ids_num = 0;
	unsigned *l2ids = NULL;

	ASSERT(num_ca != NULL);
	ASSERT(ca != NULL);
	ASSERT(max_num_ca != 0);
	ASSERT(m_cap != NULL);
	ASSERT(m_cpu != NULL);

	ret = pqos_l2ca_get_cos_num(m_cap, &count);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_RESOURCE; /* L2 CAT not supported */

	ret = resctrl_alloc_get_grps_num(m_cap, &count);
	if (ret != PQOS_RETVAL_OK)
		return ret;

	if (count > max_num_ca)
		/* Not enough space to store the classes */
		return PQOS_RETVAL_PARAM;

	l2ids = pqos_cpu_get_l2ids(m_cpu, &l2ids_num);
	if (l2ids == NULL || l2ids_num == 0) {
		ret = PQOS_RETVAL_ERROR;
		goto os_l2ca_get_exit;
	}

	if (l2id >= l2ids_num) {
		ret = PQOS_RETVAL_PARAM;
		goto os_l2ca_get_exit;
	}


        ret = resctrl_lock_shared();
        if (ret != PQOS_RETVAL_OK)
                goto os_l2ca_get_exit;

	for (class_id = 0; class_id < count; class_id++) {
		struct resctrl_alloc_schemata schmt;

		ret = resctrl_alloc_schemata_init(class_id, m_cap, m_cpu,
		                                  &schmt);
		if (ret == PQOS_RETVAL_OK)
			ret = resctrl_alloc_schemata_read(class_id, &schmt);

		if (ret == PQOS_RETVAL_OK)
			ca[class_id] = schmt.l2ca[l2id];

		resctrl_alloc_schemata_fini(&schmt);

		if (ret != PQOS_RETVAL_OK)
			goto os_l2ca_get_unlock;
	}
	*num_ca = count;

 os_l2ca_get_unlock:
        resctrl_lock_release();

 os_l2ca_get_exit:
	if (l2ids != NULL)
		free(l2ids);

	return ret;
}

int
os_l2ca_get_min_cbm_bits(unsigned *min_cbm_bits)
{
	int ret;
	char buf[128];
	const struct pqos_capability *l2_cap = NULL;
	FILE *fd;

	ASSERT(m_cap != NULL);
	ASSERT(min_cbm_bits != NULL);

	/**
	 * Get L2 CAT capabilities
	 */
	ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L2CA, &l2_cap);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_RESOURCE; /* L2 CAT not supported */

	memset(buf, 0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "%s/info/L2/min_cbm_bits", RESCTRL_PATH);

	fd = fopen(buf, "r");
	if (fd == NULL)
		return PQOS_RETVAL_ERROR;

	if (fscanf(fd, "%u", min_cbm_bits) != 1)
		ret = PQOS_RETVAL_ERROR;

	fclose(fd);

	return ret;
}

int
os_mba_set(const unsigned socket,
           const unsigned num_cos,
           const struct pqos_mba *requested,
           struct pqos_mba *actual)
{
	int ret;
	unsigned sockets_num = 0;
	unsigned *sockets = NULL;
	unsigned i, step = 0;
	unsigned num_grps = 0;
	const struct pqos_capability *mba_cap = NULL;

	ASSERT(requested != NULL);
	ASSERT(num_cos != 0);
	ASSERT(m_cap != NULL);
	ASSERT(m_cpu != NULL);

	/**
	 * Check if MBA is supported
	 */
	ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_MBA, &mba_cap);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_RESOURCE; /* MBA not supported */

	ret = resctrl_alloc_get_grps_num(m_cap, &num_grps);
	if (ret != PQOS_RETVAL_OK)
		return ret;

	if (num_cos > num_grps)
		return PQOS_RETVAL_PARAM;

        step = mba_cap->u.mba->throttle_step;

        /**
	 * Check if class id's are within allowed range.
	 */
	for (i = 0; i < num_cos; i++)
		if (requested[i].class_id >= num_grps) {
			LOG_ERROR("MBA COS%u is out of range (COS%u is max)!\n",
			          requested[i].class_id, num_grps - 1);
			return PQOS_RETVAL_PARAM;
		}

	/* Get number of sockets in the system */
	sockets = pqos_cpu_get_sockets(m_cpu, &sockets_num);
	if (sockets == NULL || sockets_num == 0 || socket >= sockets_num) {
		ret = PQOS_RETVAL_ERROR;
		goto os_l3ca_set_exit;
	}

        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                goto os_l3ca_set_exit;

	for (i = 0; i < num_cos; i++) {
		struct resctrl_alloc_schemata schmt;

		ret = resctrl_alloc_schemata_init(requested[i].class_id,
		                                  m_cap, m_cpu, &schmt);

		/* read schemata file */
		if (ret == PQOS_RETVAL_OK)
			ret = resctrl_alloc_schemata_read(requested[i].class_id,
				                          &schmt);

		/* update and write schemata */
		if (ret == PQOS_RETVAL_OK) {
			struct pqos_mba *mba = &(schmt.mba[socket]);

			*mba = requested[i];
                        mba->mb_rate = (((requested[i].mb_rate
                                          + (step/2)) / step) * step);
			if (mba->mb_rate == 0)
				mba->mb_rate = step;

			ret = resctrl_alloc_schemata_write(
				requested[i].class_id, &schmt);
		}

		if (actual != NULL) {
			/* read actual schemata */
			if (ret == PQOS_RETVAL_OK)
				ret = resctrl_alloc_schemata_read(
					requested[i].class_id, &schmt);

			/* update actual schemata */
			if (ret == PQOS_RETVAL_OK)
				actual[i] = schmt.mba[socket];
		}
		resctrl_alloc_schemata_fini(&schmt);

		if (ret != PQOS_RETVAL_OK)
			goto os_l3ca_set_unlock;
	}

 os_l3ca_set_unlock:
        resctrl_lock_release();

 os_l3ca_set_exit:
	if (sockets != NULL)
		free(sockets);

	return ret;
}

int
os_mba_get(const unsigned socket,
           const unsigned max_num_cos,
           unsigned *num_cos,
           struct pqos_mba *mba_tab)
{
	int ret;
	unsigned class_id;
	unsigned count = 0;
	unsigned sockets_num = 0;
	unsigned *sockets = NULL;
	const struct pqos_capability *mba_cap = NULL;

	ASSERT(num_cos != NULL);
	ASSERT(max_num_cos != 0);
	ASSERT(mba_tab != NULL);
	ASSERT(m_cap != NULL);
	ASSERT(m_cpu != NULL);

	/**
	 * Check if MBA is supported
	 */
	ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_MBA, &mba_cap);
	if (ret != PQOS_RETVAL_OK)
		return PQOS_RETVAL_RESOURCE; /* MBA not supported */

	ret = resctrl_alloc_get_grps_num(m_cap, &count);
	if (ret != PQOS_RETVAL_OK)
		return ret;

	if (count > max_num_cos)
		return PQOS_RETVAL_ERROR;

	sockets = pqos_cpu_get_sockets(m_cpu, &sockets_num);
	if (sockets == NULL || sockets_num == 0 || socket >= sockets_num) {
		ret = PQOS_RETVAL_ERROR;
		goto os_mba_get_exit;
	}

        ret = resctrl_lock_shared();
        if (ret != PQOS_RETVAL_OK)
                goto os_mba_get_exit;

	for (class_id = 0; class_id < count; class_id++) {
		struct resctrl_alloc_schemata schmt;

		ret = resctrl_alloc_schemata_init(class_id, m_cap, m_cpu,
		                                  &schmt);
		if (ret == PQOS_RETVAL_OK)
			ret = resctrl_alloc_schemata_read(class_id, &schmt);

		if (ret == PQOS_RETVAL_OK)
			mba_tab[class_id] = schmt.mba[socket];

		resctrl_alloc_schemata_fini(&schmt);

		if (ret != PQOS_RETVAL_OK)
			goto os_mba_get_unlock;
	}
	*num_cos = count;

 os_mba_get_unlock:
        resctrl_lock_release();

 os_mba_get_exit:
	if (sockets != NULL)
		free(sockets);

	return ret;
}

int
os_alloc_assoc_set_pid(const pid_t task,
                       const unsigned class_id)
{
        int ret;
	unsigned max_cos = 0;
        int ret_mon;
        char mon_group[256];

        ASSERT(m_cap != NULL);

	/* Get number of COS */
        ret = resctrl_alloc_get_grps_num(m_cap, &max_cos);
	if (ret != PQOS_RETVAL_OK)
		return ret;

        if (class_id >= max_cos) {
                LOG_ERROR("COS out of bounds for task %d\n", (int)task);
                return PQOS_RETVAL_PARAM;
        }

        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                return ret;

        /*
         * When task is moved to different COS we need to update monitoring
         * groups. Obtain monitoring group name
         */
        ret_mon = resctrl_mon_assoc_get_pid(task, mon_group, sizeof(mon_group));
        if (ret_mon != PQOS_RETVAL_OK && ret_mon != PQOS_RETVAL_RESOURCE)
                LOG_WARN("Failed to obtain monitoring group assigment for "
                         "task %d\n", task);

        /* Write to tasks file */
        ret = resctrl_alloc_assoc_set_pid(task, class_id);
        if (ret != PQOS_RETVAL_OK)
                goto os_alloc_assoc_set_pid_exit;

        /* Task monitoring was started assign it back to monitoring group */
        if (ret_mon == PQOS_RETVAL_OK) {
                ret_mon = resctrl_mon_assoc_set_pid(task, mon_group);
                if (ret_mon != PQOS_RETVAL_OK)
                        LOG_WARN("Could not assign task %d back to monitoring "
                                 "group\n", task);
        }

 os_alloc_assoc_set_pid_exit:
        resctrl_lock_release();

        return ret;
}

int
os_alloc_assoc_get_pid(const pid_t task,
                       unsigned *class_id)
{
        int ret;

        ASSERT(class_id != NULL);

        ret = resctrl_lock_shared();
        if (ret != PQOS_RETVAL_OK)
                return ret;

        /* Search tasks files */
        ret = resctrl_alloc_assoc_get_pid(task, class_id);

        resctrl_lock_release();

        return ret;
}

int
os_alloc_assign_pid(const unsigned technology,
                    const pid_t *task_array,
                    const unsigned task_num,
                    unsigned *class_id)
{
        unsigned i, num_rctl_grps = 0;
        int ret;

        ASSERT(task_num > 0);
        ASSERT(task_array != NULL);
        ASSERT(class_id != NULL);
        ASSERT(m_cap != NULL);
        UNUSED_PARAM(technology);

        /* obtain highest class id for all requested technologies */
        ret = resctrl_alloc_get_grps_num(m_cap, &num_rctl_grps);
        if (ret != PQOS_RETVAL_OK)
                return ret;

        if (num_rctl_grps == 0)
                return PQOS_RETVAL_ERROR;

        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                return ret;

        /* find an unused class from highest down */
        ret = get_unused_cos(num_rctl_grps - 1, class_id);
        if (ret != PQOS_RETVAL_OK)
                goto os_alloc_assign_pid_unlock;

        /* assign tasks to the unused class */
        for (i = 0; i < task_num; i++) {
                ret = resctrl_alloc_task_write(*class_id, task_array[i]);
                if (ret != PQOS_RETVAL_OK)
                        goto os_alloc_assign_pid_unlock;
        }

 os_alloc_assign_pid_unlock:
        resctrl_lock_release();

        return ret;
}

int
os_alloc_release_pid(const pid_t *task_array,
                     const unsigned task_num)
{
        unsigned i;
	int ret;

        ASSERT(task_array != NULL);
        ASSERT(task_num != 0);

        ret = resctrl_lock_exclusive();
        if (ret != PQOS_RETVAL_OK)
                return ret;

        /**
         * Write all tasks to default COS#0 tasks file
         * - return on error
         * - otherwise try next task in array
         */
        for (i = 0; i < task_num; i++) {
                ret = resctrl_alloc_task_write(0, task_array[i]);
		if (ret != PQOS_RETVAL_OK)
                        goto os_alloc_release_pid_unlock;
	}

 os_alloc_release_pid_unlock:
        resctrl_lock_release();

        return ret;
}
