/*
* 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.
*
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <dirent.h>
#include "log.h"
#include "types.h"
#include "resctrl_alloc.h"
/**
* ---------------------------------------
* Local data structures
* ---------------------------------------
*/
static const struct pqos_cap *m_cap = NULL;
static const struct pqos_cpuinfo *m_cpu = NULL;
/*
* COS file names on resctrl file system
*/
static const char *rctl_cpus = "cpus";
static const char *rctl_schemata = "schemata";
static const char *rctl_tasks = "tasks";
int
resctrl_alloc_init(const struct pqos_cpuinfo *cpu, const struct pqos_cap *cap)
{
if (cpu == NULL || cap == NULL)
return PQOS_RETVAL_PARAM;
m_cap = cap;
m_cpu = cpu;
return PQOS_RETVAL_OK;
}
int
resctrl_alloc_fini(void)
{
m_cap = NULL;
m_cpu = NULL;
return PQOS_RETVAL_OK;
}
int
resctrl_alloc_get_grps_num(const struct pqos_cap *cap, unsigned *grps_num)
{
unsigned i;
unsigned max_rctl_grps = 0;
int ret = PQOS_RETVAL_OK;
ASSERT(cap != NULL);
ASSERT(grps_num != NULL);
/*
* Loop through all caps that have OS support
* Find max COS supported by all
*/
for (i = 0; i < cap->num_cap; i++) {
unsigned num_cos = 0;
const struct pqos_capability *p_cap = &cap->capabilities[i];
if (!p_cap->os_support)
continue;
/* get L3 CAT COS num */
if (p_cap->type == PQOS_CAP_TYPE_L3CA) {
ret = pqos_l3ca_get_cos_num(cap, &num_cos);
if (ret != PQOS_RETVAL_OK)
return ret;
if (max_rctl_grps == 0)
max_rctl_grps = num_cos;
else if (num_cos < max_rctl_grps)
max_rctl_grps = num_cos;
}
/* get L2 CAT COS num */
if (p_cap->type == PQOS_CAP_TYPE_L2CA) {
ret = pqos_l2ca_get_cos_num(cap, &num_cos);
if (ret != PQOS_RETVAL_OK)
return ret;
if (max_rctl_grps == 0)
max_rctl_grps = num_cos;
else if (num_cos < max_rctl_grps)
max_rctl_grps = num_cos;
}
/* get MBA COS num */
if (p_cap->type == PQOS_CAP_TYPE_MBA) {
ret = pqos_mba_get_cos_num(cap, &num_cos);
if (ret != PQOS_RETVAL_OK)
return ret;
if (max_rctl_grps == 0)
max_rctl_grps = num_cos;
else if (num_cos < max_rctl_grps)
max_rctl_grps = num_cos;
}
}
*grps_num = max_rctl_grps;
return PQOS_RETVAL_OK;
}
/**
* @brief Converts string into 64-bit unsigned number.
*
* Numbers can be in decimal or hexadecimal format.
*
* @param [in] s string to be converted into 64-bit unsigned number
* @param [in] base Numerical base
* @param [out] Numeric value of the string representing the number
*
* @return Operational status
* @retval PQOS_RETVAL_OK on success
*/
static int
strtouint64(const char *s, int base, uint64_t *value)
{
char *endptr = NULL;
ASSERT(s != NULL);
if (strncasecmp(s, "0x", 2) == 0) {
base = 16;
s += 2;
}
*value = strtoull(s, &endptr, base);
if (!(*s != '\0' && (*endptr == '\0' || *endptr == '\n')))
return PQOS_RETVAL_ERROR;
return PQOS_RETVAL_OK;
}
/**
* @brief Opens COS file in resctl filesystem
*
* @param [in] class_id COS id
* @param [in] name File name
* @param [in] mode fopen mode
*
* @return Pointer to the stream
* @retval Pointer on success
* @retval NULL on error
*/
static FILE *
resctrl_alloc_fopen(const unsigned class_id, const char *name, const char *mode)
{
FILE *fd;
char buf[128];
int result;
ASSERT(name != NULL);
ASSERT(mode != NULL);
memset(buf, 0, sizeof(buf));
if (class_id == 0)
result = snprintf(buf, sizeof(buf) - 1,
"%s/%s", RESCTRL_PATH, name);
else
result = snprintf(buf, sizeof(buf) - 1, "%s/COS%u/%s",
RESCTRL_PATH, class_id, name);
if (result < 0)
return NULL;
fd = fopen(buf, mode);
if (fd == NULL)
LOG_ERROR("Could not open %s file %s for COS %u\n",
name, buf, class_id);
return fd;
}
/**
* @brief Closes COS file in resctl filesystem
*
* @param[in] fd File descriptor
*
* @return Operational status
* @retval PQOS_RETVAL_OK on success
*/
static int
resctrl_alloc_fclose(FILE *fd)
{
if (fd == NULL)
return PQOS_RETVAL_PARAM;
if (fclose(fd) == 0)
return PQOS_RETVAL_OK;
switch (errno) {
case EBADF:
LOG_ERROR("Invalid file descriptor!\n");
break;
case EINVAL:
LOG_ERROR("Invalid file arguments!\n");
break;
default:
LOG_ERROR("Error closing file!\n");
}
return PQOS_RETVAL_ERROR;
}
/*
* ---------------------------------------
* CPU mask structures and utility functions
* ---------------------------------------
*/
int
resctrl_alloc_cpumask_write(const unsigned class_id,
const struct resctrl_cpumask *mask)
{
int ret = PQOS_RETVAL_OK;
FILE *fd;
fd = resctrl_alloc_fopen(class_id, rctl_cpus, "w");
if (fd == NULL)
return PQOS_RETVAL_ERROR;
ret = resctrl_cpumask_write(fd, mask);
if (ret == PQOS_RETVAL_OK)
ret = resctrl_alloc_fclose(fd);
else
resctrl_alloc_fclose(fd);
return ret;
}
int
resctrl_alloc_cpumask_read(const unsigned class_id,
struct resctrl_cpumask *mask)
{
int ret;
FILE *fd;
fd = resctrl_alloc_fopen(class_id, rctl_cpus, "r");
if (fd == NULL)
return PQOS_RETVAL_ERROR;
ret = resctrl_cpumask_read(fd, mask);
if (resctrl_alloc_fclose(fd) != PQOS_RETVAL_OK)
return PQOS_RETVAL_ERROR;
return ret;
}
/*
* ---------------------------------------
* Schemata structures and utility functions
* ---------------------------------------
*/
void
resctrl_alloc_schemata_fini(struct resctrl_alloc_schemata *schemata)
{
if (schemata->l2ca != NULL) {
free(schemata->l2ca);
schemata->l2ca = NULL;
}
if (schemata->l3ca != NULL) {
free(schemata->l3ca);
schemata->l3ca = NULL;
}
if (schemata->mba != NULL) {
free(schemata->mba);
schemata->mba = NULL;
}
}
int
resctrl_alloc_schemata_init(const unsigned class_id,
const struct pqos_cap *cap,
const struct pqos_cpuinfo *cpu,
struct resctrl_alloc_schemata *schemata)
{
int ret = PQOS_RETVAL_OK;
int retval;
unsigned num_cos, num_ids, i;
ASSERT(schemata != NULL);
memset(schemata, 0, sizeof(struct resctrl_alloc_schemata));
/* L2 */
retval = pqos_l2ca_get_cos_num(cap, &num_cos);
if (retval == PQOS_RETVAL_OK && class_id < num_cos) {
unsigned *l2ids = NULL;
int cdp_enabled;
l2ids = pqos_cpu_get_l2ids(cpu, &num_ids);
if (l2ids == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_init_exit;
}
free(l2ids);
schemata->l2ca_num = num_ids;
schemata->l2ca = calloc(num_ids, sizeof(struct pqos_l2ca));
if (schemata->l2ca == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_init_exit;
}
ret = pqos_l2ca_cdp_enabled(cap, NULL, &cdp_enabled);
if (ret != PQOS_RETVAL_OK)
goto resctrl_alloc_schemata_init_exit;
/* fill class_id */
for (i = 0; i < num_ids; i++) {
schemata->l2ca[i].class_id = class_id;
schemata->l2ca[i].cdp = cdp_enabled;
}
}
/* L3 */
retval = pqos_l3ca_get_cos_num(cap, &num_cos);
if (retval == PQOS_RETVAL_OK && class_id < num_cos) {
unsigned *sockets = NULL;
int cdp_enabled;
sockets = pqos_cpu_get_sockets(cpu, &num_ids);
if (sockets == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_init_exit;
}
free(sockets);
schemata->l3ca_num = num_ids;
schemata->l3ca = calloc(num_ids, sizeof(struct pqos_l3ca));
if (schemata->l3ca == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_init_exit;
}
ret = pqos_l3ca_cdp_enabled(cap, NULL, &cdp_enabled);
if (ret != PQOS_RETVAL_OK)
goto resctrl_alloc_schemata_init_exit;
/* fill class_id and cdp values */
for (i = 0; i < num_ids; i++) {
schemata->l3ca[i].class_id = class_id;
schemata->l3ca[i].cdp = cdp_enabled;
}
}
/* MBA */
retval = pqos_mba_get_cos_num(cap, &num_cos);
if (retval == PQOS_RETVAL_OK && class_id < num_cos) {
unsigned *sockets = NULL;
sockets = pqos_cpu_get_sockets(cpu, &num_ids);
if (sockets == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_init_exit;
}
free(sockets);
schemata->mba_num = num_ids;
schemata->mba = calloc(num_ids, sizeof(struct pqos_mba));
if (schemata->mba == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_init_exit;
}
/* fill class_id */
for (i = 0; i < num_ids; i++) {
schemata->mba[i].class_id = class_id;
schemata->mba[i].mb_rate = 100;
}
}
resctrl_alloc_schemata_init_exit:
/* Deallocate memory in case of error */
if (ret != PQOS_RETVAL_OK)
resctrl_alloc_schemata_fini(schemata);
return ret;
}
/**
* @brief Schemata type
*/
enum resctrl_alloc_schemata_type {
RESCTRL_ALLOC_SCHEMATA_TYPE_NONE, /**< unknown */
RESCTRL_ALLOC_SCHEMATA_TYPE_L2, /**< L2 CAT without CDP */
RESCTRL_ALLOC_SCHEMATA_TYPE_L2CODE, /**< L2 CAT code */
RESCTRL_ALLOC_SCHEMATA_TYPE_L2DATA, /**< L2 CAT data */
RESCTRL_ALLOC_SCHEMATA_TYPE_L3, /**< L3 CAT without CDP */
RESCTRL_ALLOC_SCHEMATA_TYPE_L3CODE, /**< L3 CAT code */
RESCTRL_ALLOC_SCHEMATA_TYPE_L3DATA, /**< L3 CAT data */
RESCTRL_ALLOC_SCHEMATA_TYPE_MB, /**< MBA data */
};
/**
* @brief Determine allocation type
*
* @param [in] str resctrl label
*
* @return Allocation type
*/
static int
resctrl_alloc_schemata_type_get(const char *str)
{
int type = RESCTRL_ALLOC_SCHEMATA_TYPE_NONE;
if (strcasecmp(str, "L2") == 0)
type = RESCTRL_ALLOC_SCHEMATA_TYPE_L2;
else if (strcasecmp(str, "L2CODE") == 0)
type = RESCTRL_ALLOC_SCHEMATA_TYPE_L2CODE;
else if (strcasecmp(str, "L2DATA") == 0)
type = RESCTRL_ALLOC_SCHEMATA_TYPE_L2DATA;
else if (strcasecmp(str, "L3") == 0)
type = RESCTRL_ALLOC_SCHEMATA_TYPE_L3;
else if (strcasecmp(str, "L3CODE") == 0)
type = RESCTRL_ALLOC_SCHEMATA_TYPE_L3CODE;
else if (strcasecmp(str, "L3DATA") == 0)
type = RESCTRL_ALLOC_SCHEMATA_TYPE_L3DATA;
else if (strcasecmp(str, "MB") == 0)
type = RESCTRL_ALLOC_SCHEMATA_TYPE_MB;
return type;
}
/**
* @brief Fill schemata structure
*
* @param [in] res_id Resource id
* @param [in] value Ways mask/Memory B/W rate
* @param [in] type Schemata type
* @param [out] schemata Schemata structure
*
* @return Operational status
* @retval PQOS_RETVAL_OK on success
*/
static int
resctrl_alloc_schemata_set(const unsigned res_id,
const uint64_t value,
const int type,
struct resctrl_alloc_schemata *schemata)
{
if (type == RESCTRL_ALLOC_SCHEMATA_TYPE_L2) {
if (schemata->l2ca_num <= res_id)
return PQOS_RETVAL_ERROR;
schemata->l2ca[res_id].u.ways_mask = value;
} else if (type == RESCTRL_ALLOC_SCHEMATA_TYPE_L2CODE) {
if (schemata->l2ca_num <= res_id || !schemata->l2ca[res_id].cdp)
return PQOS_RETVAL_ERROR;
schemata->l2ca[res_id].u.s.code_mask = value;
} else if (type == RESCTRL_ALLOC_SCHEMATA_TYPE_L2DATA) {
if (schemata->l2ca_num <= res_id || !schemata->l2ca[res_id].cdp)
return PQOS_RETVAL_ERROR;
schemata->l2ca[res_id].u.s.data_mask = value;
} else if (type == RESCTRL_ALLOC_SCHEMATA_TYPE_L3) {
if (schemata->l3ca_num <= res_id || schemata->l3ca[res_id].cdp)
return PQOS_RETVAL_ERROR;
schemata->l3ca[res_id].u.ways_mask = value;
} else if (type == RESCTRL_ALLOC_SCHEMATA_TYPE_L3CODE) {
if (schemata->l3ca_num <= res_id || !schemata->l3ca[res_id].cdp)
return PQOS_RETVAL_ERROR;
schemata->l3ca[res_id].u.s.code_mask = value;
} else if (type == RESCTRL_ALLOC_SCHEMATA_TYPE_L3DATA) {
if (schemata->l3ca_num <= res_id || !schemata->l3ca[res_id].cdp)
return PQOS_RETVAL_ERROR;
schemata->l3ca[res_id].u.s.data_mask = value;
} else if (type == RESCTRL_ALLOC_SCHEMATA_TYPE_MB) {
if (schemata->mba_num <= res_id)
return PQOS_RETVAL_ERROR;
schemata->mba[res_id].mb_rate = value;
}
return PQOS_RETVAL_OK;
}
int
resctrl_alloc_schemata_read(const unsigned class_id,
struct resctrl_alloc_schemata *schemata)
{
int ret = PQOS_RETVAL_OK;
FILE *fd = NULL;
int type = RESCTRL_ALLOC_SCHEMATA_TYPE_NONE;
char *p = NULL, *q = NULL, *saveptr = NULL;
const size_t buf_size = 16 * 1024;
char *buf = calloc(buf_size, sizeof(*buf));
if (buf == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_read_exit;
}
ASSERT(schemata != NULL);
fd = resctrl_alloc_fopen(class_id, rctl_schemata, "r");
if (fd == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_read_exit;
}
if ((schemata->l3ca_num > 0 && schemata->l3ca == NULL)
|| (schemata->l2ca_num > 0 && schemata->l2ca == NULL)) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_read_exit;
}
while (fgets(buf, buf_size, fd) != NULL) {
q = buf;
/**
* Trim white spaces
*/
while (isspace(*q))
q++;
/**
* Determine allocation type
*/
p = strchr(q, ':');
if (p == NULL) {
ret = PQOS_RETVAL_ERROR;
break;
}
*p = '\0';
type = resctrl_alloc_schemata_type_get(q);
/* Skip unknown label */
if (type == RESCTRL_ALLOC_SCHEMATA_TYPE_NONE)
continue;
/**
* Parse COS masks
*/
for (++p; ; p = NULL) {
char *token = NULL;
uint64_t id = 0;
uint64_t value = 0;
unsigned base = (type == RESCTRL_ALLOC_SCHEMATA_TYPE_MB
? 10 : 16);
token = strtok_r(p, ";", &saveptr);
if (token == NULL)
break;
q = strchr(token, '=');
if (q == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_read_exit;
}
*q = '\0';
ret = strtouint64(token, 10, &id);
if (ret != PQOS_RETVAL_OK)
goto resctrl_alloc_schemata_read_exit;
ret = strtouint64(q + 1, base, &value);
if (ret != PQOS_RETVAL_OK)
goto resctrl_alloc_schemata_read_exit;
ret = resctrl_alloc_schemata_set(id,
value,
type,
schemata);
if (ret != PQOS_RETVAL_OK)
goto resctrl_alloc_schemata_read_exit;
}
}
resctrl_alloc_schemata_read_exit:
if (buf != NULL)
free(buf);
/* check if error occured */
if (ret == PQOS_RETVAL_OK)
ret = resctrl_alloc_fclose(fd);
else if (fd)
resctrl_alloc_fclose(fd);
return ret;
}
int
resctrl_alloc_schemata_write(const unsigned class_id,
const struct resctrl_alloc_schemata *schemata)
{
int ret = PQOS_RETVAL_OK;
unsigned i;
FILE *fd = NULL;
const size_t buf_size = 16 * 1024;
char *buf = calloc(buf_size, sizeof(*buf));
if (buf == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_write_exit;
}
ASSERT(schemata != NULL);
fd = resctrl_alloc_fopen(class_id, rctl_schemata, "w");
if (fd == NULL) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_write_exit;
}
/* Enable fully buffered output. File won't be flushed until 16kB
* buffer is full */
if (setvbuf(fd, buf, _IOFBF, buf_size) != 0) {
ret = PQOS_RETVAL_ERROR;
goto resctrl_alloc_schemata_write_exit;
}
/* L2 without CDP */
if (schemata->l2ca_num > 0 && !schemata->l2ca[0].cdp) {
fprintf(fd, "L2:");
for (i = 0; i < schemata->l2ca_num; i++) {
if (i > 0)
fprintf(fd, ";");
fprintf(fd, "%u=%llx", i, (unsigned long long)
schemata->l2ca[i].u.ways_mask);
}
fprintf(fd, "\n");
}
/* L2 with CDP */
if (schemata->l2ca_num > 0 && schemata->l2ca[0].cdp) {
fprintf(fd, "L2CODE:");
for (i = 0; i < schemata->l2ca_num; i++) {
if (i > 0)
fprintf(fd, ";");
fprintf(fd, "%u=%llx", i, (unsigned long long)
schemata->l2ca[i].u.s.code_mask);
}
fprintf(fd, "\nL2DATA:");
for (i = 0; i < schemata->l2ca_num; i++) {
if (i > 0)
fprintf(fd, ";");
fprintf(fd, "%u=%llx", i, (unsigned long long)
schemata->l2ca[i].u.s.data_mask);
}
fprintf(fd, "\n");
}
/* L3 without CDP */
if (schemata->l3ca_num > 0 && !schemata->l3ca[0].cdp) {
fprintf(fd, "L3:");
for (i = 0; i < schemata->l3ca_num; i++) {
if (i > 0)
fprintf(fd, ";");
fprintf(fd, "%u=%llx", i, (unsigned long long)
schemata->l3ca[i].u.ways_mask);
}
fprintf(fd, "\n");
}
/* L3 with CDP */
if (schemata->l3ca_num > 0 && schemata->l3ca[0].cdp) {
fprintf(fd, "L3CODE:");
for (i = 0; i < schemata->l3ca_num; i++) {
if (i > 0)
fprintf(fd, ";");
fprintf(fd, "%u=%llx", i, (unsigned long long)
schemata->l3ca[i].u.s.code_mask);
}
fprintf(fd, "\nL3DATA:");
for (i = 0; i < schemata->l3ca_num; i++) {
if (i > 0)
fprintf(fd, ";");
fprintf(fd, "%u=%llx", i, (unsigned long long)
schemata->l3ca[i].u.s.data_mask);
}
fprintf(fd, "\n");
}
/* MBA */
if (schemata->mba_num > 0) {
fprintf(fd, "MB:");
for (i = 0; i < schemata->mba_num; i++) {
if (i > 0)
fprintf(fd, ";");
fprintf(fd, "%u=%u", i, schemata->mba[i].mb_rate);
}
fprintf(fd, "\n");
}
resctrl_alloc_schemata_write_exit:
/* check if error occured */
if (ret == PQOS_RETVAL_OK)
ret = resctrl_alloc_fclose(fd);
else if (fd)
resctrl_alloc_fclose(fd);
/* setvbuf buffer should be freed after fclose */
if (buf != NULL)
free(buf);
return ret;
}
/**
* ---------------------------------------
* Task utility functions
* ---------------------------------------
*/
int
resctrl_alloc_task_validate(const pid_t task)
{
DIR *dir;
char buf[64];
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf)-1, "/proc/%d", (int)task);
dir = opendir(buf);
if (dir == NULL)
return PQOS_RETVAL_ERROR;
closedir(dir);
if (access(buf, F_OK) != 0)
return PQOS_RETVAL_ERROR;
return PQOS_RETVAL_OK;
}
int
resctrl_alloc_task_write(const unsigned class_id, const pid_t task)
{
FILE *fd;
int ret;
/* Check if task exists */
ret = resctrl_alloc_task_validate(task);
if (ret != PQOS_RETVAL_OK) {
LOG_ERROR("Task %d does not exist!\n", (int)task);
return PQOS_RETVAL_PARAM;
}
/* Open resctrl tasks file */
fd = resctrl_alloc_fopen(class_id, rctl_tasks, "w");
if (fd == NULL)
return PQOS_RETVAL_ERROR;
/* Write task ID to file */
if (fprintf(fd, "%d\n", task) < 0) {
LOG_ERROR("Failed to write to task %d to file!\n", (int) task);
resctrl_alloc_fclose(fd);
return PQOS_RETVAL_ERROR;
}
ret = resctrl_alloc_fclose(fd);
return ret;
}
unsigned *
resctrl_alloc_task_read(unsigned class_id, unsigned *count)
{
FILE *fd;
unsigned *tasks = NULL, idx = 0;
int ret;
char buf[128];
struct linked_list {
uint64_t task_id;
struct linked_list *next;
} head, *current = NULL;
/* Open resctrl tasks file */
fd = resctrl_alloc_fopen(class_id, rctl_tasks, "r");
if (fd == NULL)
return NULL;
head.next = NULL;
current = &head;
memset(buf, 0, sizeof(buf));
while (fgets(buf, sizeof(buf), fd) != NULL) {
uint64_t tmp;
struct linked_list *p = NULL;
ret = strtouint64(buf, 10, &tmp);
if (ret != PQOS_RETVAL_OK)
goto resctrl_alloc_task_read_exit_clean;
p = malloc(sizeof(head));
if (p == NULL)
goto resctrl_alloc_task_read_exit_clean;
p->task_id = tmp;
p->next = NULL;
current->next = p;
current = p;
idx++;
}
/* if no pids found then allocate empty buffer to be returned */
if (idx == 0)
tasks = (unsigned *) calloc(1, sizeof(tasks[0]));
else
tasks = (unsigned *) malloc(idx * sizeof(tasks[0]));
if (tasks == NULL)
goto resctrl_alloc_task_read_exit_clean;
*count = idx;
current = head.next;
idx = 0;
while (current != NULL) {
tasks[idx++] = current->task_id;
current = current->next;
}
resctrl_alloc_task_read_exit_clean:
resctrl_alloc_fclose(fd);
current = head.next;
while (current != NULL) {
struct linked_list *tmp = current->next;
free(current);
current = tmp;
}
return tasks;
}
int
resctrl_alloc_task_search(unsigned *class_id,
const struct pqos_cap *cap,
const pid_t task)
{
FILE *fd;
unsigned i, max_cos = 0;
int ret;
/* Check if task exists */
ret = resctrl_alloc_task_validate(task);
if (ret != PQOS_RETVAL_OK) {
LOG_ERROR("Task %d does not exist!\n", (int)task);
return PQOS_RETVAL_PARAM;
}
/* Get number of COS */
ret = resctrl_alloc_get_grps_num(cap, &max_cos);
if (ret != PQOS_RETVAL_OK)
return ret;
/**
* Starting at highest COS - search all COS tasks files for task ID
*/
for (i = (max_cos - 1); (int)i >= 0; i--) {
uint64_t tid = 0;
char buf[128];
/* Open resctrl tasks file */
fd = resctrl_alloc_fopen(i, rctl_tasks, "r");
if (fd == NULL)
return PQOS_RETVAL_ERROR;
/* Search tasks file for specified task ID */
memset(buf, 0, sizeof(buf));
while (fgets(buf, sizeof(buf), fd) != NULL) {
ret = strtouint64(buf, 10, &tid);
if (ret != PQOS_RETVAL_OK)
continue;
if (task == (pid_t)tid) {
*class_id = i;
if (resctrl_alloc_fclose(fd) != PQOS_RETVAL_OK)
return PQOS_RETVAL_ERROR;
return PQOS_RETVAL_OK;
}
}
if (resctrl_alloc_fclose(fd) != PQOS_RETVAL_OK)
return PQOS_RETVAL_ERROR;
}
/* If not found in any COS group - return error */
LOG_ERROR("Failed to get association for task %d!\n", (int)task);
return PQOS_RETVAL_ERROR;
}
int
resctrl_alloc_task_file_check(const unsigned class_id, unsigned *found)
{
FILE *fd;
char buf[128];
/* Open resctrl tasks file */
fd = resctrl_alloc_fopen(class_id, rctl_tasks, "r");
if (fd == NULL)
return PQOS_RETVAL_ERROR;
/* Search tasks file for any task ID */
memset(buf, 0, sizeof(buf));
if (fgets(buf, sizeof(buf), fd) != NULL)
*found = 1;
if (resctrl_alloc_fclose(fd) != PQOS_RETVAL_OK)
return PQOS_RETVAL_ERROR;
return PQOS_RETVAL_OK;
}
int
resctrl_alloc_assoc_set(const unsigned lcore, const unsigned class_id)
{
int ret;
struct resctrl_cpumask mask;
ret = resctrl_alloc_cpumask_read(class_id, &mask);
if (ret != PQOS_RETVAL_OK)
return ret;
resctrl_cpumask_set(lcore, &mask);
ret = resctrl_alloc_cpumask_write(class_id, &mask);
return ret;
}
int
resctrl_alloc_assoc_get(const unsigned lcore, unsigned *class_id)
{
int ret;
unsigned grps;
unsigned i;
struct resctrl_cpumask mask;
ASSERT(m_cap != NULL);
ret = resctrl_alloc_get_grps_num(m_cap, &grps);
if (ret != PQOS_RETVAL_OK)
return ret;
for (i = 0; i < grps; i++) {
ret = resctrl_alloc_cpumask_read(i, &mask);
if (ret != PQOS_RETVAL_OK)
return ret;
if (resctrl_cpumask_get(lcore, &mask)) {
*class_id = i;
return PQOS_RETVAL_OK;
}
}
return ret;
}
int
resctrl_alloc_assoc_set_pid(const pid_t task, const unsigned class_id)
{
/* Write to tasks file */
return resctrl_alloc_task_write(class_id, task);
}
int
resctrl_alloc_assoc_get_pid(const pid_t task, unsigned *class_id)
{
/* Search tasks files */
return resctrl_alloc_task_search(class_id, m_cap, task);
}