Blob Blame History Raw
/*
 *   BSD LICENSE
 *
 *   Copyright(c) 2016-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.
 */

#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <pqos.h>

#include "rdt.h"
#include "cpu.h"

static const struct pqos_cap *m_cap;
static const struct pqos_cpuinfo *m_cpu;
static const struct pqos_capability *m_cap_l2ca;
static const struct pqos_capability *m_cap_l3ca;
static const struct pqos_capability *m_cap_mba;

/**
 * @brief Prints L2, L3 or MBA configuration in \a cfg
 *
 * @param [in] stream to write output to
 * @param [in] cfg COS configuration
 */
static void
rdt_cfg_print(FILE *stream, const struct rdt_cfg cfg)
{
	if (NULL == cfg.u.generic_ptr)
		return;

	switch (cfg.type) {
	case PQOS_CAP_TYPE_L2CA:
		if (cfg.u.l2->cdp == 1)
			fprintf(stream, "code MASK: 0x%llx, data MASK: 0x%llx",
				(unsigned long long) cfg.u.l2->u.s.code_mask,
				(unsigned long long) cfg.u.l2->u.s.data_mask);
		else
			fprintf(stream, "MASK: 0x%llx",
				(unsigned long long) cfg.u.l2->u.ways_mask);
		break;

	case PQOS_CAP_TYPE_L3CA:
		if (cfg.u.l3->cdp == 1)
			fprintf(stream, "code MASK: 0x%llx, data MASK: 0x%llx",
				(unsigned long long) cfg.u.l3->u.s.code_mask,
				(unsigned long long) cfg.u.l3->u.s.data_mask);
		else
			fprintf(stream, "MASK: 0x%llx",
				(unsigned long long) cfg.u.l3->u.ways_mask);
		break;

	case PQOS_CAP_TYPE_MBA:
		fprintf(stream, "RATE: %u", cfg.u.mba->mb_rate);
		break;

	default:
		break;
	}
}
/**
 * @brief Gets short string representation of configuration type of \a cfg
 *
 * @param [in] cfg COS allocation configuration
 *
 * @return String representation of configuration type
 */
static const char *
rdt_cfg_get_type_str(const struct rdt_cfg cfg)
{
	if (NULL == cfg.u.generic_ptr)
		return "ERROR!";

	switch (cfg.type) {
	case PQOS_CAP_TYPE_L2CA:
		return "L2";

	case PQOS_CAP_TYPE_L3CA:
		return "L3";

	case PQOS_CAP_TYPE_MBA:
		return "MBA";

	default:
		return "ERROR!";
	}
}

/**
 * @brief Validates configuration in \a cfg
 *
 * @param [in] cfg COS configuration
 *
 * @return Result
 * @retval 1 on success
 * @retval 0 on failure
 */
static int
rdt_cfg_is_valid(const struct rdt_cfg cfg)
{
	if (cfg.u.generic_ptr == NULL)
		return 0;

	switch (cfg.type) {
	case PQOS_CAP_TYPE_L2CA:
		/* Validate L2 CAT configuration.
		 *  - If CDP is enabled code_mask and data_mask must be set
		 *  - If CDP is not enabled ways_mask must be set
		 */
		return cfg.u.l2 != NULL &&
			((cfg.u.l2->cdp == 1 && cfg.u.l2->u.s.code_mask != 0 &&
			cfg.u.l2->u.s.data_mask != 0) ||
			(cfg.u.l2->cdp == 0 && cfg.u.l2->u.ways_mask != 0));

	case PQOS_CAP_TYPE_L3CA:
		/* Validate L3 CAT configuration.
		 *  - If CDP is enabled code_mask and data_mask must be set
		 *  - If CDP is not enabled ways_mask must be set
		 */
		return cfg.u.l3 != NULL &&
			((cfg.u.l3->cdp == 1 && cfg.u.l3->u.s.code_mask != 0 &&
			cfg.u.l3->u.s.data_mask != 0) ||
			(cfg.u.l3->cdp == 0 && cfg.u.l3->u.ways_mask != 0));

	case PQOS_CAP_TYPE_MBA:
		return cfg.u.mba != NULL && cfg.u.mba->mb_rate > 0 &&
			cfg.u.mba->mb_rate <= 100;

	default:
		break;
	}

	return 0;
}

/**
 * @brief Returns number of set bits in \a bitmask
 *
 * @param [in] bitmask
 *
 * @return bits count
 */
static unsigned
bits_count(uint64_t bitmask)
{
	unsigned count = 0;

	for (; bitmask != 0; count++)
		bitmask &= bitmask - 1;

	return count;
}

/**
 * @brief Tests if \a bitmask is contiguous
 *
 * @param [in] cat_type string to identify CAT type (L2 or L3) on error
 * @param [in] bitmask bitmask to be tested
 *
 * @return Result
 * @retval 1 on success
 * @retval 0 on failure
 */
static int
is_contiguous(const char *cat_type, const uint64_t bitmask)
{
	/* check if bitmask is contiguous */
	unsigned i = 0, j = 0;
	const unsigned max_idx = (sizeof(bitmask) * CHAR_BIT);

	if (bitmask == 0)
		return 0;

	for (i = 0; i < max_idx; i++) {
		if (((1ULL << i) & bitmask) != 0)
			j++;
		else if (j > 0)
			break;
	}

	if (bits_count(bitmask) != j) {
		fprintf(stderr, "Allocation: %s CAT mask 0x%llx is not "
			"contiguous.\n", cat_type,
			(unsigned long long) bitmask);
		return 0;
	}

	return 1;
}

/**
 * @brief Function to get the highest resource ID (socket/cluster)
 *
 * @param technology used to determine if res ID should be socket or cluster
 *
 * @return highest resource ID
 * @retval positive on success
 * @retval 0 on error
 */
static int
get_max_res_id(unsigned technology)
{
        unsigned num_ids, max = 0, *ids = NULL;

        if (m_cpu == NULL || technology == 0)
                return 0;

        /* get number of L2IDs */
        if (technology & (1 << PQOS_CAP_TYPE_L2CA)) {
                ids = pqos_cpu_get_l2ids(m_cpu, &num_ids);
                if (ids == NULL)
                        return 0;

                max = num_ids > max ? num_ids : max;
                free(ids);
        }
        /* get number of sockets */
        if (technology & (1 << PQOS_CAP_TYPE_L3CA) ||
            technology & (1 << PQOS_CAP_TYPE_MBA)) {
                ids = pqos_cpu_get_sockets(m_cpu, &num_ids);
                if (ids == NULL)
                        return 0;

                max = num_ids > max ? num_ids : max;
                free(ids);
        }
        return max;
}

/**
 * @brief Converts string \a str to UINT
 *
 * @param [in] str string
 * @param [in] base numerical base
 * @param [out] value INT value
 *
 * @return number of parsed characters
 * @retval positive on success
 * @retval negative on error (-errno)
 */
static int
str_to_uint64(const char *str, int base, uint64_t *value)
{
	const char *str_start = str;
	char *str_end = NULL;

	if (NULL == str || NULL == value)
		return -EINVAL;

	while (isblank(*str_start))
		str_start++;

	if (base == 10 && !isdigit(*str_start))
		return -EINVAL;

	if (base == 16 && !isxdigit(*str_start))
		return -EINVAL;

	errno = 0;
	*value = strtoull(str_start, &str_end, base);
	if (errno != 0 || str_end == NULL || str_end == str_start)
		return -EINVAL;

	return str_end - str;
}

/**
 * @brief Parses CBM (Capacity Bit Mask) string \a cbm
 *        and stores result in \a mask and \a cmask
 *
 * @param [in] cbm CBM string
 * @param [in] force_dual_mask expect two masks separated with ','
 * @param [out] mask common (L2 or L3 non CDP) or data mask (L3 CDP)
 * @param [out] cmask code mask (L3 CDP)
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
parse_mask_set(const char *cbm, const int force_dual_mask, uint64_t *mask,
               uint64_t *cmask)
{
	int offset = 0;
	const char *cbm_start = cbm;

	/* parse mask_set from start point */
	if (*cbm_start == '(' || force_dual_mask) {
		while (!isxdigit(*cbm_start))
			cbm_start++;

		offset = str_to_uint64(cbm_start, 16, cmask);
		if (offset < 0)
			goto err;

		cbm_start += offset;

		while (isblank(*cbm_start))
			cbm_start++;

		if (*cbm_start != ',')
			goto err;

		cbm_start++;
	}

	offset = str_to_uint64(cbm_start, 16, mask);
	if (offset < 0)
		goto err;

	return cbm_start + offset - cbm;

 err:
	return -EINVAL;
}

int
parse_reset(const char *cpustr)
{
	unsigned cpustr_len = strlen(cpustr);
	int ret = 0;

	ret = str_to_cpuset(cpustr, cpustr_len, &g_cfg.reset_cpuset);
	return ret > 0 ? 0 : ret;
}

/**
 * @brief Parses CBMs string \a param and stores in \a ca
 *
 * @param [in] param CBMs string
 * @param [out] ca to store result
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
rdt_ca_str_to_cbm(const char *param, struct rdt_cfg ca)
{
	uint64_t mask = 0, mask2 = 0;
	int ret, force_dual_mask;

	if (!(PQOS_CAP_TYPE_L2CA == ca.type || PQOS_CAP_TYPE_L3CA == ca.type) ||
            NULL == ca.u.generic_ptr || NULL == param)
		return -EINVAL;

	force_dual_mask = (NULL != strchr(param, ','));
	ret = parse_mask_set(param, force_dual_mask, &mask, &mask2);
	if (ret < 0)
		return -EINVAL;

	if (mask == 0 || is_contiguous(rdt_cfg_get_type_str(ca), mask) == 0)
		return -EINVAL;

	if (mask2 != 0 && is_contiguous(rdt_cfg_get_type_str(ca), mask2) == 0)
		return -EINVAL;

	if (PQOS_CAP_TYPE_L2CA == ca.type) {
		if (mask2 != 0) {
			ca.u.l2->cdp = 1;
			ca.u.l2->u.s.data_mask = mask;
			ca.u.l2->u.s.code_mask = mask2;
		} else
			ca.u.l2->u.ways_mask = mask;
	} else {
		if (mask2 != 0) {
			ca.u.l3->cdp = 1;
			ca.u.l3->u.s.data_mask = mask;
			ca.u.l3->u.s.code_mask = mask2;
		} else
			ca.u.l3->u.ways_mask = mask;
	}

	return 0;
}

/**
 * @brief Parses rate string \a param and stores in \a mba
 *
 * @param [in] param rate string
 * @param [out] mba to store result
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
rdt_mba_str_to_rate(const char *param, struct rdt_cfg mba)
{
	uint64_t rate;
	int ret;

	if (PQOS_CAP_TYPE_MBA != mba.type || NULL == mba.u.generic_ptr ||
			NULL == param)
		return -EINVAL;

	ret = str_to_uint64(param, 10, &rate);
	if (ret < 0 || rate == 0 || rate > 100)
		return -EINVAL;

	mba.u.mba->mb_rate = rate;

	return 0;
}

/*
 * @brief Simplifies feature string
 *
 * @param [in] feature feature string
 *
 * @return simplified feature as char
 * @retval char representation of feature
 * @retval '?' on error
 */
static char
simplify_feature_str(const char *feature)
{
	static const struct {
		const char *feature_long;
		const char feature_char;
	} feature_lut[] = {
		{"cpu", 'c'},
		{"l2", '2'},
		{"l3", '3'},
		{"mba", 'm'},
		{NULL, 0}
	};


	if (feature == NULL)
		return '?';

	if (strlen(feature) > 1) {
		unsigned i = 0;

		while (feature_lut[i].feature_long != NULL) {
			if (strcmp(feature, feature_lut[i].feature_long) == 0)
				return feature_lut[i].feature_char;

			i++;
		}
	} else
		return feature[0];

	return '?';
}

int
parse_rdt(char *rdtstr)
{
	char *rdtstr_saveptr = NULL;
	char *group = NULL;
	unsigned idx = g_cfg.config_count;
	const struct rdt_cfg l2ca = wrap_l2ca(&g_cfg.config[idx].l2);
	const struct rdt_cfg l3ca = wrap_l3ca(&g_cfg.config[idx].l3);
	const struct rdt_cfg mba = wrap_mba(&g_cfg.config[idx].mba);
	const unsigned min_len_group = strlen("3=f");
	int ret;

	if (rdtstr == NULL)
		return -EINVAL;

	group = strtok_r(rdtstr, ";", &rdtstr_saveptr);

	while (group != NULL) {
		char *group_saveptr = NULL;

		/* Min len check, 1 feature "3=f" */
		if (strlen(group) < min_len_group) {
			fprintf(stderr, "Invalid option: \"%s\"\n", group);
			return -EINVAL;
		}

		const char *feature = strtok_r(group, "=", &group_saveptr);
		const char *param = strtok_r(NULL, "=", &group_saveptr);

		if (feature == NULL || param == NULL) {
			fprintf(stderr, "Invalid option: \"%s\"\n", group);
			return -EINVAL;
		}

		switch (simplify_feature_str(feature)) {
		case '2':
			if (rdt_cfg_is_valid(l2ca))
				return -EINVAL;

			ret = rdt_ca_str_to_cbm(param, l2ca);
			if (ret < 0)
				return ret;
			break;

		case '3':
			if (rdt_cfg_is_valid(l3ca))
				return -EINVAL;

			ret = rdt_ca_str_to_cbm(param, l3ca);
			if (ret < 0)
				return ret;
			break;

		case 'c':
			if (CPU_COUNT(&g_cfg.config[idx].cpumask) != 0)
				return -EINVAL;

			ret = str_to_cpuset(param, strlen(param),
                                            &g_cfg.config[idx].cpumask);
			if (ret <= 0)
				return -EINVAL;

			if (CPU_COUNT(&g_cfg.config[idx].cpumask) == 0)
				return -EINVAL;
			break;

		case 'm':
			if (rdt_cfg_is_valid(mba))
				return -EINVAL;

			ret = rdt_mba_str_to_rate(param, mba);
			if (ret < 0)
				return ret;
			break;

		default:
			fprintf(stderr, "Invalid option: \"%s\"\n", feature);
			return -EINVAL;
                }

		group = strtok_r(NULL, ";", &rdtstr_saveptr);
	}

        /* if no cpus specified then set pid flag */
	if (CPU_COUNT(&g_cfg.config[idx].cpumask) == 0)
                g_cfg.config[idx].pid_cfg = 1;

        if (!(rdt_cfg_is_valid(l2ca) || rdt_cfg_is_valid(l3ca) ||
              rdt_cfg_is_valid(mba)))
		return -EINVAL;

	g_cfg.config_count++;

	return 0;
}

/**
 * @brief Checks if configured CPU sets are overlapping
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
check_cpus_overlapping(void)
{
	unsigned i = 0;

	for (i = 0; i < g_cfg.config_count; i++) {
		unsigned j = 0;

		for (j = i + 1; j < g_cfg.config_count; j++) {
			unsigned overlapping = 0;
#ifdef __linux__
			cpu_set_t mask;

			CPU_AND(&mask, &g_cfg.config[i].cpumask,
				&g_cfg.config[j].cpumask);
			overlapping = CPU_COUNT(&mask) != 0;
#endif
#ifdef __FreeBSD__
			overlapping = CPU_OVERLAP(&g_cfg.config[i].cpumask,
                                                  &g_cfg.config[j].cpumask);
#endif
			if (1 == overlapping) {
				fprintf(stderr, "Allocation: Requested CPUs "
					"sets are overlapping.\n");
				return -EINVAL;
			}
		}
	}

	return 0;
}

/**
 * @brief Checks if configured CPUs are valid and have no COS associated
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
check_cpus(void)
{
	unsigned i = 0;
	int ret = 0;

	for (i = 0; i < g_cfg.config_count; i++) {
		unsigned cpu_id = 0;

		for (cpu_id = 0; cpu_id < CPU_SETSIZE; cpu_id++) {
			unsigned cos_id = 0;

			if (CPU_ISSET(cpu_id, &g_cfg.config[i].cpumask) == 0)
				continue;

			ret = pqos_cpu_check_core(m_cpu, cpu_id);
			if (ret != PQOS_RETVAL_OK) {
				fprintf(stderr, "Allocation: %u is not a valid "
					"logical core id.\n", cpu_id);
				return -ENODEV;
			}

			ret = pqos_alloc_assoc_get(cpu_id, &cos_id);
			if (ret != PQOS_RETVAL_OK) {
				fprintf(stderr, "Allocation: Failed to read "
					"cpu %u COS association.\n", cpu_id);
				return -EFAULT;
			}

			/*
			 * Check if COS assigned to lcore is different
			 * then default one (#0)
			 */
			if (cos_id != 0) {
				fprintf(stderr, "Allocation: cpu %u has "
					"already associated COS#%u. Please "
					"reset allocation."
					"\n", cpu_id, cos_id);
				return -EBUSY;
			}
		}
	}

	return 0;
}

/**
 * @brief Checks if CPU supports requested CDP configuration
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
check_cdp_support(void)
{
	unsigned i = 0;
	const int cdp_supported = m_cap_l3ca != NULL &&
		m_cap_l3ca->u.l3ca->cdp == 1;
	const int cdp_enabled = cdp_supported &&
		m_cap_l3ca->u.l3ca->cdp_on == 1;

	if (cdp_enabled)
		return 0;

	for (i = 0; i < g_cfg.config_count; i++) {
		if (0 == g_cfg.config[i].l3.cdp)
			continue;

		if (!cdp_supported)
			fprintf(stderr, "Allocation: CDP requested but not "
				"supported.\n");
		else
			fprintf(stderr, "Allocation: CDP requested but not "
				"enabled. Please enable CDP.\n");

		return -ENOTSUP;
	}

	return 0;
}

/**
 * @brief Checks if CAT/MBA configuration requested by a user via cmd line
 *        is supported by the system.
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
check_supported(void)
{
	unsigned i = 0;

	for (i = 0; i < g_cfg.config_count; i++) {
		if (rdt_cfg_is_valid(wrap_l3ca(&g_cfg.config[i].l3)) &&
                    NULL == m_cap_l3ca) {
			fprintf(stderr, "Allocation: L3CA requested but not "
				"supported by system!\n");
			return -ENOTSUP;
		}

		if (rdt_cfg_is_valid(wrap_l2ca(&g_cfg.config[i].l2)) &&
                    NULL == m_cap_l2ca) {
			fprintf(stderr, "Allocation: L2CA requested but not "
				"supported by system!\n");
			return -ENOTSUP;
		}

		if (rdt_cfg_is_valid(wrap_mba(&g_cfg.config[i].mba)) &&
                    NULL == m_cap_mba) {
			fprintf(stderr, "Allocation: MBA requested but not "
				"supported by system!\n");
			return -ENOTSUP;
		}
	}

	return 0;
}

/**
 * @brief Returns negation of max CBM for requested \a type (L2 or L3)
 *
 * @param [in] type type of pqos CAT capability, L2 or L3
 *
 * @return mask
 * @retval UINT64_MAX on error
 */
static uint64_t
get_not_cbm(const enum pqos_cap_type type)
{
	if (PQOS_CAP_TYPE_L2CA == type && NULL != m_cap_l2ca)
		return (UINT64_MAX << (m_cap_l2ca->u.l2ca->num_ways));
	else if (PQOS_CAP_TYPE_L3CA == type && NULL != m_cap_l3ca)
		return (UINT64_MAX << (m_cap_l3ca->u.l3ca->num_ways));
	else
		return UINT64_MAX;
}

/**
 * @brief Returns contention mask for requested \a type (L2 or L3)
 *
 * @param [in] type type of pqos CAT capability, L2 or L3
 *
 * @return contention mask
 * @retval UINT64_MAX on error
 *
 */
static uint64_t
get_contention_mask(const enum pqos_cap_type type)
{
	if (PQOS_CAP_TYPE_L2CA == type && NULL != m_cap_l2ca)
		return m_cap_l2ca->u.l2ca->way_contention;
	else if (PQOS_CAP_TYPE_L3CA == type && NULL != m_cap_l3ca)
		return m_cap_l3ca->u.l3ca->way_contention;
	else
		return UINT64_MAX;
}

/**
 * @brief Returns cumulative mask for \a ca CAT config
 *
 * For L2/L3 CDP config returns code_mask | data_mask,
 * for L2/L3 non-CDP config returns ways_mask
 *
 * @param [in] ca CAT COS configuration
 *
 * @return cumulative mask
 * @retval UINT64_MAX on error
 */
static uint64_t
rdt_ca_get_cumulative_cbm(const struct rdt_cfg ca)
{
	if (!(PQOS_CAP_TYPE_L2CA == ca.type || PQOS_CAP_TYPE_L3CA == ca.type) ||
            NULL == ca.u.generic_ptr || 0 == rdt_cfg_is_valid(ca))
		return UINT64_MAX;

	if (PQOS_CAP_TYPE_L2CA == ca.type) {
		if (ca.u.l2->cdp == 1)
			return ca.u.l2->u.s.code_mask | ca.u.l2->u.s.data_mask;
		else
			return ca.u.l2->u.ways_mask;
	}

	if (ca.u.l3->cdp == 1)
		return ca.u.l3->u.s.code_mask | ca.u.l3->u.s.data_mask;

	return ca.u.l3->u.ways_mask;
}

/**
 * @brief Checks if requested CBMs of \a type are supported by system.
 *
 * Warn if CBM overlaps contention mask
 *
 * @param [in] type type of pqos CAT capability, L2 or L3
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
check_cbm_len_and_contention(const enum pqos_cap_type type)
{
	unsigned i = 0;
	struct rdt_cfg ca;
	uint64_t mask = 0;

	if (!(PQOS_CAP_TYPE_L2CA == type || PQOS_CAP_TYPE_L3CA == type))
		return -EINVAL;

	const uint64_t not_cbm = get_not_cbm(type);
	const uint64_t contention_cbm = get_contention_mask(type);

	if (UINT64_MAX == not_cbm || UINT64_MAX == contention_cbm)
		return -EINVAL;

	for (i = 0; i < g_cfg.config_count; i++) {
		if (PQOS_CAP_TYPE_L2CA == type)
			ca = wrap_l2ca(&g_cfg.config[i].l2);
		else
			ca = wrap_l3ca(&g_cfg.config[i].l3);

		if (!rdt_cfg_is_valid(ca))
			continue;

		mask = rdt_ca_get_cumulative_cbm(ca);
		if (UINT64_MAX == mask)
			return -EFAULT;

		if ((mask & not_cbm) != 0) {
			fprintf(stderr, "CAT: One or more of requested %s CBMs "
				"(", rdt_cfg_get_type_str(ca));
			rdt_cfg_print(stderr, ca);
			fprintf(stderr, ") not supported by system "
				"(too long).\n");
			return -ENOTSUP;
		}

		/* Just a note */
		if ((mask & contention_cbm) != 0) {
			printf("CAT: One or more of requested %s CBMs "
                               "(", rdt_cfg_get_type_str(ca));
			rdt_cfg_print(stdout, ca);
			printf(") overlap contention mask.\n");
		}
	}

	return 0;
}

/**
 * @brief Checks if requested CBMs (of all types) are supported by system
 *
 * Warn if CBM overlaps contention mask
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
check_cbm_len_and_contention_all(void)
{
	int ret;

	if (NULL != m_cap_l2ca) {
		ret = check_cbm_len_and_contention(PQOS_CAP_TYPE_L2CA);
		if (ret < 0)
			return ret;
	}

	if (NULL != m_cap_l3ca) {
		ret = check_cbm_len_and_contention(PQOS_CAP_TYPE_L3CA);
		if (ret < 0)
			return ret;
	}

	return 0;
}


/**
 * @brief Validates requested CAT/MBA configuration
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
alloc_validate(void)
{
	int ret = 0;

	ret = check_cpus();
	if (ret != 0)
		return ret;

	ret = check_supported();
	if (ret != 0)
		return ret;

	ret = check_cdp_support();
	if (ret != 0)
		return ret;

	ret = check_cbm_len_and_contention_all();
	if (ret != 0)
		return ret;

	ret = check_cpus_overlapping();
	if (ret != 0)
		return ret;

	return 0;
}

/**
 * @brief Gets cores from \a cores set which belong to \a socket
 *
 * @param [in] cores set of cores
 * @param [in] socket_id socket to get cores from
 * @param [out] core_num number of cores in \a core_array
 * @param [out] core_array array of cores
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
get_socket_cores(const cpu_set_t *cores, const unsigned socket_id,
                 unsigned *core_num, unsigned core_array[CPU_SETSIZE])
{
	unsigned i;

	if (cores == NULL || core_num == NULL || core_array == NULL)
		return -EINVAL;

	if (m_cpu == NULL)
		return -EFAULT;

	if (CPU_COUNT(cores) == 0) {
		*core_num = 0;
		return 0;
	}

	for (i = 0, *core_num = 0; i < m_cpu->num_cores; i++) {
		if (m_cpu->cores[i].socket != socket_id ||
                    0 == CPU_ISSET(m_cpu->cores[i].lcore, cores))
			continue;

		core_array[(*core_num)++] = m_cpu->cores[i].lcore;
	}

	return 0;
}

/**
 * @brief Gets cores from \a cores set which belong to \a l2_id
 *
 * @param [in] cores set of cores
 * @param [in] l2_id L2 cluster ID to get cores from
 * @param [out] core_num number of cores in \a core_array
 * @param [out] core_array array of cores
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
get_l2id_cores(const cpu_set_t *cores, const unsigned l2_id,
               unsigned *core_num, unsigned core_array[CPU_SETSIZE])
{
	unsigned i;

	if (cores == NULL || core_num == NULL || core_array == NULL)
		return -EINVAL;

	if (m_cpu == NULL)
		return -EFAULT;

	if (CPU_COUNT(cores) == 0) {
		*core_num = 0;
		return 0;
	}

	for (i = 0, *core_num = 0; i < m_cpu->num_cores; i++) {
		if (m_cpu->cores[i].l2_id != l2_id ||
                    0 == CPU_ISSET(m_cpu->cores[i].lcore, cores))
			continue;

		core_array[(*core_num)++] = m_cpu->cores[i].lcore;
	}

	return 0;
}

/**
 * @brief pqos_alloc_release wrapper
 *
 * @param [in] cores set of cores
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
alloc_release(const cpu_set_t *cores)
{
	unsigned core_array[CPU_SETSIZE];
	unsigned core_num, i;
	int ret;

	if (cores == NULL)
		return -EINVAL;

	if (m_cpu == NULL)
		return -EFAULT;

        /* if no cores in set then return success */
        if (CPU_COUNT(cores) == 0)
                return 0;

	for (i = 0, core_num = 0; i < m_cpu->num_cores; i++) {
		if (0 == CPU_ISSET(m_cpu->cores[i].lcore, cores))
			continue;

		core_array[core_num++] = m_cpu->cores[i].lcore;
	}

	ret = pqos_alloc_release(core_array, core_num);
	if (ret != PQOS_RETVAL_OK) {
                fprintf(stderr, "Failed to release COS!\n");
                return -EFAULT;
        }
	return 0;
}

/**
 * @brief Get default COS
 *
 * @param [out] l2_def L2 COS
 * @param [out] l3_def L3 COS
 * @param [out] mba_def MBA COS
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
alloc_get_default_cos(struct pqos_l2ca *l2_def, struct pqos_l3ca *l3_def,
                    struct pqos_mba *mba_def)
{
	if (l2_def == NULL && l3_def == NULL && mba_def == NULL)
		return -EINVAL;

	if (m_cap_l2ca == NULL && m_cap_l3ca == NULL && m_cap_mba == NULL)
		return -EFAULT;

	if (m_cap_l2ca != NULL && l2_def != NULL) {
		const uint64_t def_mask =
			(1ULL << m_cap_l2ca->u.l2ca->num_ways) - 1ULL;

		memset(l2_def, 0, sizeof(*l2_def));

		if (m_cap_l2ca->u.l2ca->cdp_on == 1) {
			l2_def->cdp = 1;
			l2_def->u.s.code_mask = def_mask;
			l2_def->u.s.data_mask = def_mask;
		} else
			l2_def->u.ways_mask = def_mask;
	}

	if (m_cap_l3ca != NULL  && l3_def != NULL) {
		const uint64_t def_mask =
			(1ULL << m_cap_l3ca->u.l3ca->num_ways) - 1ULL;

		memset(l3_def, 0, sizeof(*l3_def));

		if (m_cap_l3ca->u.l3ca->cdp_on == 1) {
			l3_def->cdp = 1;
			l3_def->u.s.code_mask = def_mask;
			l3_def->u.s.data_mask = def_mask;
		} else
			l3_def->u.ways_mask = def_mask;
	}

	if (m_cap_mba != NULL && mba_def != NULL) {
		memset(mba_def, 0, sizeof(*mba_def));
		mba_def->mb_rate = 100;
	}

	return 0;
}

/**
 * @brief Configures COS \a cos_id defined by \a l2ca, \a l3ca and \a mba on
 *        \a socket_id
 *
 * @param [in] l2ca L2 classes configuration
 * @param [in] l3ca L3 classes configuration
 * @param [in] mba MBA configuration
 * @param [in] core_id core belonging to a socket/L2 cluster to configure COS on
 * @param [in] cos_id id of COS to be configured
 *
 * @return status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
cfg_configure_cos(const struct pqos_l2ca *l2ca, const struct pqos_l3ca *l3ca,
    const struct pqos_mba *mba, const unsigned core_id, const unsigned cos_id)
{
	struct pqos_l2ca l2_defs;
	struct pqos_l3ca l3_defs;
	struct pqos_mba mba_defs;
	const struct pqos_coreinfo *ci = NULL;
	int ret;

	if (NULL == l2ca || NULL == l3ca)
		return -EINVAL;

	if (NULL == m_cap_l2ca && NULL == m_cap_l3ca)
		return -EFAULT;


        ci = pqos_cpu_get_core_info(m_cpu, core_id);
        if (ci == NULL) {
                fprintf(stderr, "Error getting information about core %u!\n",
                        core_id);
                return -EINVAL;
        }

        /* Get default COS values */
	ret = alloc_get_default_cos(&l2_defs, &l3_defs, &mba_defs);
	if (ret != 0)
		return ret;

	/* Configure COS */
	if (m_cap_l3ca != NULL && m_cap_l3ca->u.l3ca->num_classes > cos_id) {
		const unsigned socket_id = ci->socket;
		struct pqos_l3ca ca = *l3ca;

		/* if COS is not configured, set it to default */
		if (!rdt_cfg_is_valid(wrap_l3ca(&ca)))
			ca = l3_defs;

		/* set proper COS id */
		ca.class_id = cos_id;

		ret = pqos_l3ca_set(socket_id, 1, &ca);
		if (ret != PQOS_RETVAL_OK) {
			fprintf(stderr,
				"Error setting L3 CAT COS#%u on socket %u!\n",
				cos_id, socket_id);
			return -EFAULT;
		}
	}

	if (m_cap_l2ca != NULL  && m_cap_l2ca->u.l2ca->num_classes > cos_id) {
		const unsigned l2_id = ci->l2_id;
		struct pqos_l2ca ca = *l2ca;

		/* if COS is not configured, set it to default */
		if (!rdt_cfg_is_valid(wrap_l2ca(&ca)))
			ca = l2_defs;

		/* set proper COS id */
		ca.class_id = cos_id;

		if (pqos_l2ca_set(l2_id, 1, &ca) != PQOS_RETVAL_OK) {
			fprintf(stderr,
				"Error setting L2 CAT COS#%u on L2ID %u!\n",
				cos_id, l2_id);
			return -EFAULT;
		}
	}

	if (m_cap_mba != NULL && m_cap_mba->u.mba->num_classes > cos_id) {
		const unsigned socket_id = ci->socket;
		struct pqos_mba mba_requested = *mba;
		struct pqos_mba mba_actual;

		/* if COS is not configured, set it to default */
		if (!rdt_cfg_is_valid(wrap_mba(&mba_requested)))
			mba_requested = mba_defs;

		/* set proper COS id */
		mba_requested.class_id = cos_id;

		ret = pqos_mba_set(socket_id, 1, &mba_requested, &mba_actual);
		if (ret != PQOS_RETVAL_OK) {
			fprintf(stderr,
				"Error setting MBA COS#%u on socket %u!\n",
				cos_id, socket_id);
			return -EFAULT;
		}
	}

	return 0;
}

/**
 * @brief Sets socket/cluster definitions for selected cores (OS interface)
 *        Note: Assigns COS on a system wide basis
 *
 * @param [in] technology configured RDT allocation technologies
 * @param [in] cores cores configuration table
 * @param [in] l2ca L2 CAT configuration table
 * @param [in] l3ca L3 CAT configuration table
 * @param [in] mba MBA configuration table
 *
 * @return Operation status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
cfg_set_cores_os(const unsigned technology, const cpu_set_t *cores,
                 const struct pqos_l2ca *l2ca, const struct pqos_l3ca *l3ca,
                 const struct pqos_mba *mba)
{
	int ret;
        unsigned i, max_id, core_num, cos_id;
        unsigned core_array[CPU_SETSIZE] = {0};

        /* Convert cpu set to core array */
        for (i = 0, core_num = 0; i < m_cpu->num_cores; i++) {
		if (0 == CPU_ISSET(m_cpu->cores[i].lcore, cores))
			continue;
		core_array[core_num++] = m_cpu->cores[i].lcore;
	}
        if (core_num == 0)
                return -EFAULT;

        /* Assign all cores to single COS */
        ret = pqos_alloc_assign(technology, core_array, core_num, &cos_id);
        switch (ret) {
        case PQOS_RETVAL_OK:
                break;
        case PQOS_RETVAL_RESOURCE:
                fprintf(stderr, "No free COS available!\n");
                return -EBUSY;
        default:
                fprintf(stderr, "Unable to assign COS!\n");
                return -EFAULT;
        }

        max_id = get_max_res_id(technology);
        if (!max_id)
                return -EFAULT;

        /* Set COS definition on necessary sockets/clusters */
        for (i = 0; i < max_id; i++) {
                memset(core_array, 0, sizeof(core_array));

                /* Get cores on res id i */
                if (technology & (1 << PQOS_CAP_TYPE_L2CA))
                        ret = get_l2id_cores(cores, i, &core_num, core_array);
                else
                        ret = get_socket_cores(cores, i, &core_num, core_array);
                if (ret != 0)
                        return ret;

                if (core_num == 0)
                        continue;

                /* Configure COS on res ID i */
                ret = cfg_configure_cos(l2ca, l3ca, mba, core_array[0], cos_id);
                if (ret != 0)
                        return ret;
        }

        return 0;
}

/**
 * @brief Sets socket/cluster definitions for selected cores (MSR interface)
 *        Note: Assigns COS on a res ID basis
 *
 * @param [in] technology configured RDT allocation technologies
 * @param [in] cores cores configuration table
 * @param [in] l2ca L2 CAT configuration table
 * @param [in] l3ca L3 CAT configuration table
 * @param [in] mba MBA configuration table
 *
 * @return Operation status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
cfg_set_cores_msr(const unsigned technology, const cpu_set_t *cores,
                  const struct pqos_l2ca *l2ca, const struct pqos_l3ca *l3ca,
                  const struct pqos_mba *mba)
{
	int ret;
        unsigned i, max_id;

        max_id = get_max_res_id(technology);
        if (!max_id)
                return -EFAULT;

        /* Assign new COS for all applicable res ids */
        for (i = 0; i < max_id; i++) {
                unsigned core_array[CPU_SETSIZE] = {0};
                unsigned core_num, cos_id;

                /* Get cores on res id i */
                if (technology & (1 << PQOS_CAP_TYPE_L2CA))
                        ret = get_l2id_cores(cores, i, &core_num, core_array);
                else
                        ret = get_socket_cores(cores, i, &core_num, core_array);
                if (ret != 0)
                        return ret;

                if (core_num == 0)
                        continue;

                /* Assign COS to those cores */
                ret = pqos_alloc_assign(technology, core_array, core_num,
                                        &cos_id);
                switch (ret) {
                case PQOS_RETVAL_OK:
                        break;
                case PQOS_RETVAL_RESOURCE:
                        fprintf(stderr, "No free COS available!\n");
                        return -EBUSY;
                default:
                        fprintf(stderr, "Unable to assign COS!\n");
                        return -EFAULT;
                }

                /* Configure COS on res ID i */
                ret = cfg_configure_cos(l2ca, l3ca, mba, core_array[0], cos_id);
                if (ret != 0)
                        return ret;
        }

        return 0;
}

/**
 * @brief Sets socket/cluster definitions for selected PIDs
 *
 * @param [in] technology configured RDT allocation technologies
 * @param [in] l2ca L2 CAT configuration table
 * @param [in] l3ca L3 CAT configuration table
 * @param [in] mba MBA configuration table
 *
 * @return Operation status
 * @retval 0 on success
 * @retval negative on error (-errno)
 */
static int
cfg_set_pids(const unsigned technology, const struct pqos_l3ca *l3ca,
             const struct pqos_l2ca *l2ca, const struct pqos_mba *mba)
{
        int ret = 0;
	unsigned i, cos_id, max_id, num_pids = 1;
        pid_t pid = getpid(), *p = &pid;

        /* Assign COS to selected pids otherwise assign to this pid */
        if (g_cfg.pid_count) {
                p = g_cfg.pids;
                num_pids = g_cfg.pid_count;
        }

        ret = pqos_alloc_assign_pid(technology, p, num_pids, &cos_id);
        switch (ret) {
        case PQOS_RETVAL_OK:
                break;
        case PQOS_RETVAL_RESOURCE:
                fprintf(stderr, "No free COS available!.\n");
                ret = -EBUSY;
                goto set_pids_exit;
        default:
                fprintf(stderr, "Unable to assign task to COS!\n");
                ret = -EFAULT;
                goto set_pids_exit;
        }

        max_id = get_max_res_id(technology);
        if (!max_id)
                return -EFAULT;

        /* Set COS definitions across all res IDs */
        for (i = 0; i < max_id; i++) {
                unsigned core;

                /* Get cores on res ID i */
                if (technology & (1 << PQOS_CAP_TYPE_L2CA))
                        ret = pqos_cpu_get_one_by_l2id(m_cpu, i, &core);
                else
                        ret = pqos_cpu_get_one_core(m_cpu, i, &core);
                if (ret != 0)
                        break;

                /* Configure COS on res ID i */
                ret = cfg_configure_cos(l2ca, l3ca, mba, core, cos_id);
                if (ret != 0)
                        break;
        }
set_pids_exit:
        if (ret != 0)
                (void) pqos_alloc_release_pid(p, num_pids);

        return ret;
}

int
alloc_configure(void)
{
	struct pqos_l3ca l3ca[g_cfg.config_count];
	struct pqos_l2ca l2ca[g_cfg.config_count];
	struct pqos_mba mba[g_cfg.config_count];
        cpu_set_t cpu[g_cfg.config_count];
        int pid_cfg[g_cfg.config_count];
	unsigned i = 0;
	int ret = 0;

        memset(pid_cfg, 0, g_cfg.config_count * sizeof(pid_cfg[0]));

	/* Validate cmd line configuration */
	ret = alloc_validate();
	if (ret != 0) {
		fprintf(stderr, "Requested configuration is not valid!\n");
		return ret;
	}

	for (i = 0; i < g_cfg.config_count; i++) {
		l3ca[i] = g_cfg.config[i].l3;
		l2ca[i] = g_cfg.config[i].l2;
		mba[i] = g_cfg.config[i].mba;
                cpu[i] = g_cfg.config[i].cpumask;
                pid_cfg[i] = g_cfg.config[i].pid_cfg;
        }

	for (i = 0; i < g_cfg.config_count; i++) {
                unsigned technology = 0;

		if (rdt_cfg_is_valid(wrap_l2ca(&l2ca[i])))
			technology |= (1 << PQOS_CAP_TYPE_L2CA);

		if (rdt_cfg_is_valid(wrap_l3ca(&l3ca[i])))
			technology |= (1 << PQOS_CAP_TYPE_L3CA);

		if (rdt_cfg_is_valid(wrap_mba(&mba[i])))
			technology |= (1 << PQOS_CAP_TYPE_MBA);

                /* If pid config selected then assign tasks otherwise cores */
                if (pid_cfg[i])
                        ret = cfg_set_pids(technology, &l3ca[i], &l2ca[i],
                                           &mba[i]);
                else
                        if (g_cfg.interface == PQOS_INTER_MSR)
                                ret = cfg_set_cores_msr(technology, &cpu[i],
                                                        &l2ca[i], &l3ca[i],
                                                        &mba[i]);
                        else
                                ret = cfg_set_cores_os(technology, &cpu[i],
                                                       &l2ca[i], &l3ca[i],
                                                       &mba[i]);

                /* If assign fails then free already assigned cpus */
                if (ret != 0) {
                        fprintf(stderr, "Allocation failed!\n");
                        printf("Reverting configuration of allocation...\n");
                        for (; (int)i >= 0; i--) {
                                if (pid_cfg[i])
                                        continue;
                                (void)alloc_release(&cpu[i]);
                        }
                        return ret;
                }
        }

        return 0;
}

int
alloc_reset(void)
{
	unsigned i = 0;
	int ret = 0;

	/* Associate COS#0 to cores */
	for (i = 0; i < CPU_SETSIZE; i++) {
		if (0 == CPU_ISSET(i, &g_cfg.reset_cpuset))
			continue;

		ret = pqos_alloc_assoc_set(i, 0);
		if (ret != PQOS_RETVAL_OK) {
			fprintf(stderr, "Error associating COS,"
				"core: %u, COS: 0!\n", i);
			return -EFAULT;
		}
	}

	return 0;
}

void
alloc_fini(void)
{
	int ret = 0;

	if (g_cfg.verbose)
		printf("Shutting down PQoS library...\n");

	/* deallocate all the resources */
	ret = pqos_fini();
	if (ret != PQOS_RETVAL_OK && ret != PQOS_RETVAL_INIT)
		fprintf(stderr, "Error shutting down PQoS library!\n");

	m_cap = NULL;
	m_cpu = NULL;
	m_cap_l3ca = NULL;
	memset(g_cfg.config, 0, sizeof(g_cfg.config));
	g_cfg.config_count = 0;
}

void
alloc_exit(void)
{
	unsigned i = 0;

	/* if lib is not initialized, do nothing */
	if (m_cap == NULL && m_cpu == NULL)
		return;

	if (g_cfg.verbose)
		printf("CAT: Reverting CAT configuration...\n");

        /* release resources */
	for (i = 0; i < g_cfg.config_count; i++) {
                /* release pids if selected */
                if (g_cfg.config[i].pid_cfg && g_cfg.pid_count != 0) {
                        if (pqos_alloc_release_pid(g_cfg.pids, g_cfg.pid_count))
                                fprintf(stderr, "Failed to release PID COS!\n");
                        continue;
                }
                /* release cores */
                if (alloc_release(&g_cfg.config[i].cpumask) != 0)
                        fprintf(stderr, "Failed to release cores COS!\n");
        }
	alloc_fini();
}

/**
 * @brief Signal handler to do clean-up on exit on signal
 *
 * @param [in] signum signal
 */
static void
signal_handler(int signum)
{
	if (signum == SIGINT || signum == SIGTERM) {
		printf("\nRDTSET: Signal %d received, preparing to exit...\n",
                       signum);

		alloc_exit();

		/* exit with the expected status */
		signal(signum, SIG_DFL);
		kill(getpid(), signum);
	}
}

int
alloc_init(void)
{
	int ret = 0;
	struct pqos_config cfg;

	if (m_cap != NULL || m_cpu != NULL) {
		fprintf(stderr, "Allocation: module already initialized!\n");
		return -EEXIST;
	}

	/* PQoS Initialization - Check and initialize allocation capabilities */
	memset(&cfg, 0, sizeof(cfg));
	cfg.fd_log = STDOUT_FILENO;
	cfg.verbose = 0;
	cfg.interface = g_cfg.interface;
	ret = pqos_init(&cfg);
	if (ret != PQOS_RETVAL_OK) {
		fprintf(stderr, "Allocation: Error initializing PQoS "
			"library!\n");
		ret = -EFAULT;
		goto err;
	}

	/* Get capability and CPU info pointer */
	ret = pqos_cap_get(&m_cap, &m_cpu);
	if (ret != PQOS_RETVAL_OK || m_cap == NULL || m_cpu == NULL) {
		fprintf(stderr, "Allocation: Error retrieving PQoS "
			"capabilities!\n");
		ret = -EFAULT;
		goto err;
	}

	/* Get L2CA capabilities */
	ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L2CA, &m_cap_l2ca);
	if (g_cfg.verbose && (ret != PQOS_RETVAL_OK || m_cap_l2ca == NULL))
		printf("Allocation: L2 CAT capability not supported.\n");

	/* Get L3CA capabilities */
	ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L3CA, &m_cap_l3ca);
	if (g_cfg.verbose && (ret != PQOS_RETVAL_OK || m_cap_l3ca == NULL))
		printf("Allocation: L3 CAT capability not supported.\n");

	/* Get MBA capabilities */
	ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_MBA, &m_cap_mba);
	if (g_cfg.verbose && (ret != PQOS_RETVAL_OK || m_cap_mba == NULL))
		printf("Allocation: MBA capability not supported.\n");


	if (m_cap_l3ca == NULL && m_cap_l2ca == NULL && m_cap_mba == NULL) {
		fprintf(stderr, "Allocation capabilities not supported!\n");
		ret = -EFAULT;
		goto err;
	}

	signal(SIGINT, signal_handler);
	signal(SIGTERM, signal_handler);

	ret = atexit(alloc_exit);
	if (ret != 0) {
		ret = -EFAULT;
		fprintf(stderr, "Allocation: Cannot set exit function\n");
		goto err;
	}

	return 0;

 err:
	/* deallocate all the resources */
	alloc_fini();
	return ret;
}

void
print_cmd_line_rdt_config(void)
{
	char cpustr[CPU_SETSIZE * 3];
	unsigned i;

	if (CPU_COUNT(&g_cfg.reset_cpuset) != 0) {
		cpuset_to_str(cpustr, sizeof(cpustr), &g_cfg.reset_cpuset);
		printf("Allocation Reset: CPUs: %s\n", cpustr);
	}

	for (i = 0; i < g_cfg.config_count; i++) {
		struct rdt_cfg cfg_array[3];
		unsigned j;

		if (CPU_COUNT(&g_cfg.config[i].cpumask) == 0)
			continue;

		cpuset_to_str(cpustr, sizeof(cpustr), &g_cfg.config[i].cpumask);

		cfg_array[0] = wrap_l2ca(&g_cfg.config[i].l2);
		cfg_array[1] = wrap_l3ca(&g_cfg.config[i].l3);
		cfg_array[2] = wrap_mba(&g_cfg.config[i].mba);

		for (j = 0; j < DIM(cfg_array); j++) {
			if (!rdt_cfg_is_valid(cfg_array[j]))
				continue;
			printf("%s Allocation: CPUs: %s ",
                               rdt_cfg_get_type_str(cfg_array[j]), cpustr);
			rdt_cfg_print(stdout, cfg_array[j]);
			printf("\n");
		}
	}
}