diff options
author | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-04-01 23:08:29 +0200 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-04-01 23:08:29 +0200 |
commit | b47b7db0e0fdde6cd75f8e72d149af04c1ab48f1 (patch) | |
tree | 7e825722b718678fe8da0a45d1b56cb95d3eacc3 |
Initial import
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r-- | src/dtpm/Makefile | 34 | ||||
-rw-r--r-- | src/dtpm/dtpm-rl.c | 536 | ||||
-rw-r--r-- | src/dtpm/dtpm.c | 326 | ||||
-rw-r--r-- | src/dtpm/dtpm.h | 4 | ||||
-rw-r--r-- | src/dtpm/dtpm_conf.c | 237 | ||||
-rw-r--r-- | src/dtpm/dtpm_conf.h | 9 | ||||
-rw-r--r-- | src/dtpm/dtpm_log.h | 13 | ||||
-rw-r--r-- | src/dtpm/log.h | 13 |
8 files changed, 1172 insertions, 0 deletions
diff --git a/src/dtpm/Makefile b/src/dtpm/Makefile new file mode 100644 index 0000000..054a779 --- /dev/null +++ b/src/dtpm/Makefile @@ -0,0 +1,34 @@ +# +# Copyright (C) 2021, Linaro Limited +# Author: Daniel Lezcano <daniel.lezcano@linaro.org> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +CFLAGS?=-g -Wall -Wunused-parameter +LDFLAGS?=-lconfig +CC=gcc + +OBJS = dtpm.o dtpm_conf.o + +default: dtpm + +%.o: %.c + $(CROSS_COMPILE)$(CC) -c -o $@ $< $(CFLAGS) + +dtpm: $(OBJS) + $(CROSS_COMPILE)$(CC) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS) + +clean: + rm -f $(OBJS) dtpm diff --git a/src/dtpm/dtpm-rl.c b/src/dtpm/dtpm-rl.c new file mode 100644 index 0000000..6293d52 --- /dev/null +++ b/src/dtpm/dtpm-rl.c @@ -0,0 +1,536 @@ +/* + * Power Energy Model : protocode + * + * Rebalancing power + * + * Copyright: Linaro Ltd, Daniel Lezcano <daniel.lezcano@linaro.org> + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <readline/readline.h> +#include <readline/history.h> + +#include <linux/errno.h> + +typedef unsigned long long u64; + +#define DIV_ROUND_CLOSEST(x, divisor)( \ +{ \ + typeof(x) __x = x; \ + typeof(divisor) __d = divisor; \ + (((typeof(x))-1) > 0 || \ + ((typeof(divisor))-1) > 0 || \ + (((__x) > 0) == ((__d) > 0))) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ +} \ +) + +#define BUG abort +#define MAX_CHILDREN 8 + +struct powercap_em { + struct powercap_em *parent; + struct powercap_em *children[MAX_CHILDREN]; + const char *name; + int weight; + int nr_children; + u64 min_power; + u64 max_power; + u64 cur_power; + u64 power_limit; + u64 static_power_limit; + u64 dynamic_power_limit; +}; + +struct powercap_em soc = { + .name = "soc" +}; + +struct powercap_em package = { + .name = "package" +}; + +struct powercap_em big = { + .name = "big", + .min_power = 113, + .max_power = 1130, + .cur_power = 113, + }; + +struct powercap_em little = { + .name = "little", + .min_power = 26, + .max_power = 179, + .cur_power = 150, +}; + +struct powercap_em memory = { + .name = "memory", + .min_power = 400, + .max_power = 700, + .cur_power = 700, +}; + +u64 powercap_em_get_power(struct powercap_em *pcem) +{ + u64 power = 0; + int i; + + /* + * Only the leaves of the tree can return the real power, + * otherwise we return the sum of the children power. + */ + if (!pcem->nr_children) + return pcem->cur_power; + + for (i = 0; i < pcem->nr_children; i++) + power += powercap_em_get_power(pcem->children[i]); + + return power; +} + +int powercap_em_rebalance_weight(struct powercap_em *pcem) +{ + struct powercap_em *child; + int i; + + for (i = 0; i < pcem->nr_children; i++) { + child = pcem->children[i]; + child->weight = DIV_ROUND_CLOSEST(child->max_power * 1024, + pcem->max_power); + } + + return 0; +} + +int powercap_em_set_static_power_limit(struct powercap_em *pcem, u64 power); + +int powercap_em_rebalance_limit(struct powercap_em *pcem) +{ + if (!pcem) + BUG(); + + if (pcem->power_limit) + return powercap_em_set_static_power_limit(pcem, pcem->power_limit); + + return powercap_em_rebalance_limit(pcem->parent); +} + +int powercap_em_rebalance_power(struct powercap_em *pcem) +{ + struct powercap_em *donees[MAX_CHILDREN]; + struct powercap_em *donors[MAX_CHILDREN]; + int nr_donee, nr_donor, sum_weight; + u64 power, power_free; + int i, j; + +again: + nr_donee = nr_donor = sum_weight = 0; + + /* + * This first pass build the list of the donees and the sum of + * their weights. + */ + for (i = 0; i < pcem->nr_children; i++) { + struct powercap_em *child; + + child = pcem->children[i]; + power = powercap_em_get_power(child); + + if (power > child->dynamic_power_limit) { + + /* + * We have one device which is consuming more + * energy than allocated but actually it + * donated the free power it had to another + * device. Now it is requesting more power, so + * it must get back the power it gave. Reset + * the dynamic power limit to the static power + * limit and do the rebalancing again. + */ + if (power < child->static_power_limit) { + powercap_em_rebalance_limit(pcem); + goto again; + } + + donees[nr_donee++] = child; + sum_weight += child->weight; + continue; + } + + if (power < child->dynamic_power_limit) + donors[nr_donor++] = child; + } + + /* + * There are no donees, so no need to rebalance the power + * across the actors, this is done, bail out. + */ + if (!nr_donee) + return 0; + + /* + * We are in a situation where there is no more free power for + * the donees, so we need to reduce their power. + */ + if (!nr_donor) { + + /* + * We are in the situation where all the power was + * rebalanced along the actors but more power is + * consumed, in this case we need to cap their power. + */ + for (i = 0; i < nr_donee; i++) + donees[i]->cur_power = + donees[i]->dynamic_power_limit; + + return 0; + } + + /* + * One or more actors are consuming more than the allocated + * power. Let's distribute the free power not used by the + * other actors which are below their limit. + */ + for (i = 0; i < nr_donor; i++) { + + struct powercap_em *donor = donors[i]; + + power = powercap_em_get_power(donor); + + /* + * Compute the unused power ... + */ + power_free = donor->dynamic_power_limit - power; + + /* + * ... and as it will be given to the donees, + * remove this power from the allocated budget from + * the donor. + */ + donor->dynamic_power_limit = power; + + /* + * Redistribute the unused power to the donees. + */ + for (j = 0; j < nr_donee; j++) { + donees[j]->dynamic_power_limit += + DIV_ROUND_CLOSEST( + power_free * donees[j]->weight, + sum_weight); + } + } + + /* + * Let's find out new donees and new donors + */ + goto again; + + /* Never reached */ + return -EINVAL; +} + +int powercap_em_set_power(struct powercap_em *pcem, u64 power) +{ + /* + * This function can only be called by the leaves of the + * hierarchy tree which represent the devices on the + * system. Other nodes in the tree are aggregating the + * constraints and power information of the children. + */ + if (pcem->nr_children) + return -EINVAL; + + if (power < pcem->min_power || power > pcem->max_power) + return -EINVAL; + + pcem->cur_power = power; + + /* + * The power of this device changed, let's check if there is a + * limit to rebalance the power. + */ + while (pcem) { + + if (pcem->power_limit) + return powercap_em_rebalance_power(pcem); + + pcem = pcem->parent; + } + + return 0; +} + +int powercap_em_set_static_power_limit(struct powercap_em *pcem, u64 power_limit) +{ + struct powercap_em *child; + u64 static_power_limit; + int i, ret; + + if (!pcem->nr_children) { + pcem->dynamic_power_limit = power_limit; + pcem->static_power_limit = power_limit; + return 0; + } + + for (i = 0; i < pcem->nr_children; i++) { + child = pcem->children[i]; + + static_power_limit = DIV_ROUND_CLOSEST( + power_limit * child->weight, 1024); + + ret = powercap_em_set_static_power_limit(child, + static_power_limit); + if (ret) + return ret; + } + + return 0; +} + +int powercap_em_set_power_limit(struct powercap_em *pcem, u64 power_limit) +{ + int ret; + + /* + * A power limit set to zero means we remove the constraint + */ + if (power_limit && (power_limit < pcem->min_power || + power_limit > pcem->max_power)) + return -EINVAL; + + ret = powercap_em_set_static_power_limit(pcem, power_limit); + if (ret) + return ret; + + ret = powercap_em_rebalance_power(pcem); + if (ret) + goto out_undo; + + pcem->power_limit = power_limit; + + return 0; + +out_undo: + powercap_em_set_static_power_limit(pcem, 0); + + return ret; +} + +int powercap_em_add_child(struct powercap_em *parent, struct powercap_em *child) +{ + if (parent->nr_children == MAX_CHILDREN) + return -ENOMEM; + + child->parent = parent; + parent->children[parent->nr_children] = child; + parent->nr_children++; + parent->max_power += child->max_power; + parent->min_power += child->min_power; + parent->cur_power += child->cur_power; + + return powercap_em_rebalance_weight(parent); +} + +int __powercap_em_dump(struct powercap_em *pcem, int indent) +{ + int i; + int step = 5; + printf("%*c", indent, ' '); + printf("name: %s\n", pcem->name); + printf("%*c", indent + step, ' '); + printf("| weight: %d\n", pcem->weight); + printf("%*c", indent + step, ' '); + printf("| max power: %llu\n", pcem->max_power); + printf("%*c", indent + step, ' '); + printf("| min power: %llu\n", pcem->min_power); + printf("%*c", indent + step, ' '); + printf("| cur power: %llu\n", powercap_em_get_power(pcem)); + printf("%*c", indent + step, ' '); + printf("| power limit: %llu\n", pcem->power_limit); + printf("%*c", indent + step, ' '); + printf("| static power limit: %llu\n", pcem->static_power_limit); + printf("%*c", indent + step, ' '); + printf("` dynamic power limit: %llu\n", pcem->dynamic_power_limit); + + for (i = 0; i < pcem->nr_children; i++) + __powercap_em_dump(pcem->children[i], indent + step); + + return 0; +} + +int powercap_em_dump(struct powercap_em *pcem) +{ + if (!pcem) + return -EINVAL; + + return __powercap_em_dump(pcem, 0); +} + +/****************************** readline ***************************/ + +struct powercap_em *powercap_em_lookup(struct powercap_em *pcem, + const char *name) +{ + int i; + + if (!strcmp(name, pcem->name)) + return pcem; + + for (i = 0; i < pcem->nr_children; i++) { + struct powercap_em *match; + + match = powercap_em_lookup(pcem->children[i], name); + if (match) + return match; + } + + return NULL; +} + +static int command_set_power_limit(char *argv[]) +{ + struct powercap_em *pcem; + + if (!argv[0] || !argv[1]) + return -EINVAL; + + pcem = powercap_em_lookup(&soc, argv[0]); + if (!pcem) + return -EINVAL; + + return powercap_em_set_power_limit(pcem, atoi(argv[1])); +} + +static int command_set_power(char *argv[]) +{ + struct powercap_em *pcem; + + if (!argv[0] || !argv[1]) + return -EINVAL; + + pcem = powercap_em_lookup(&soc, argv[0]); + if (!pcem) + return -EINVAL; + + return powercap_em_set_power(pcem, atoi(argv[1])); +} + +struct cmd_callback { + const char *cmd; + int (*callback)(char *argv[]); +}; + + +static struct cmd_callback set_commands[] = { + { "power", command_set_power }, + { "power_limit", command_set_power_limit }, +}; + +static int command_set(char *argv[]) +{ + int i; + + if (!argv[0]) + return -EINVAL; + + for (i = 0; i < sizeof(set_commands) / sizeof(set_commands[0]); i++) + if (!strcmp(set_commands[i].cmd, argv[0])) + return set_commands[i].callback(&argv[1]); + + return -EINVAL; +} + +static int command_dump(char *argv[]) +{ + struct powercap_em *pcem; + if (!argv[0]) + return powercap_em_dump(&soc); + + pcem = powercap_em_lookup(&soc, argv[0]); + printf("Found pcem %s\n", pcem->name); + + return powercap_em_dump(powercap_em_lookup(&soc, argv[0])); +} + +static struct cmd_callback commands[] = { + { "set", command_set }, + { "dump", command_dump }, +}; + +static int mainloop(void) +{ + char *argv[32]; + char* buf; + + read_history(".dtpm_history"); + + for (;;) { + + int i = 0; + + buf = readline("dtpm:$ "); + if (!buf) + break; + + if (strlen(buf) > 0) + add_history(buf); + + argv[i] = strtok(buf," "); + + while (argv[i]) + argv[++i] = strtok(NULL, " "); + + if (argv[0]) { + for (i = 0; i < (sizeof(commands) / sizeof(commands[0])); i++) { + if (strcmp(commands[i].cmd, argv[0])) + continue; + + if (commands[i].callback(&argv[1])) + fprintf(stderr, "Command failed\n"); + } + } + + free(buf); + } + + write_history(".dtpm_history"); + + return 0; +} + +int main(int argc, char *argv[]) +{ + if (powercap_em_add_child(&package, &big)) { + fprintf(stderr, "Failed to add child '%s' to '%s'", + big.name, package.name); + return -1; + } + + if (powercap_em_add_child(&package, &little)) { + fprintf(stderr, "Failed to add child '%s' to '%s'", + little.name, package.name); + return -1; + } + + if (powercap_em_add_child(&package, &memory)) { + fprintf(stderr, "Failed to add child '%s' to '%s'", + memory.name, package.name); + return -1; + } + + if (powercap_em_add_child(&soc, &package)) { + fprintf(stderr, "Failed to add child '%s' to '%s'", + big.name, package.name); + return -1; + } + + powercap_em_dump(&soc); + + mainloop(); + + return 0; +} diff --git a/src/dtpm/dtpm.c b/src/dtpm/dtpm.c new file mode 100644 index 0000000..12e0127 --- /dev/null +++ b/src/dtpm/dtpm.c @@ -0,0 +1,326 @@ +#define _GNU_SOURCE +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/types.h> +#include <unistd.h> +#include <dirent.h> +#include <mntent.h> +#include <pwd.h> + +#include "dtpm_log.h" +#include "dtpm_conf.h" + +#define DTPM_CHILDREN_MAX 16 +#define DTPM_CONSTRAINT_MAX 1 + +typedef unsigned long long u64; + +struct dtpm_field { + const char *filename; + const char *fmt; + union { + u64 u64val; + char name[MAXNAMLEN]; + }; + void *ptr; +}; + +struct dtpm_constraint { + struct dtpm_field name; + struct dtpm_field power_limit_uw; + struct dtpm_field time_window_us; + struct dtpm_field max_power_uw; +}; + +struct dtpm_node { + char *path; + struct dtpm_node *parent; + struct dtpm_node *children[DTPM_CHILDREN_MAX]; + struct dtpm_constraint constraints[DTPM_CONSTRAINT_MAX]; + struct dtpm_field max_power_range_uw; + struct dtpm_field power_uw; + struct dtpm_field name; +}; + +typedef int (*filter_t)(const char *); +typedef int (*dtpm_file_cb_t)(const char *, void *); +typedef int (*dtpm_node_cb_t)(struct dtpm_node *, void *); + +static int dtpm_file_rw(const char *filename, int rw, const char *fmt, ...) +{ + const char *mode = rw == O_RDONLY ? "r" : "w"; + FILE *f; + va_list args; + int ret; + + f = fopen(filename, mode); + if (!f) { + ERROR("Failed to open %s:%m\n", filename); + return -1; + } + + va_start (args, fmt); + ret = rw == O_RDONLY ? vfscanf(f, fmt, args) : vfprintf(f, fmt, args); + va_end(args); + + fclose(f); + + return ret == EOF; +} + +int dtpm_node_add_child(struct dtpm_node *node, struct dtpm_node *child) +{ + int i; + + if (!node) + return 0; + + for (i = 0; i < DTPM_CHILDREN_MAX; i++) { + + if (node->children[i]) + continue; + + node->children[i] = child; + + return 0; + } + + return -1; +} + +struct dtpm_node *dtpm_node_alloc(void) +{ + struct dtpm_node *node; + int i; + + node = malloc(sizeof(*node)); + if (!node) + return NULL; + + memset(node, 0, sizeof(*node)); + + node->name.filename = "name"; + node->name.fmt = "%s"; + node->name.ptr = node->name.name; + + node->power_uw.filename = "power_uw"; + node->power_uw.fmt = "%llu"; + node->power_uw.ptr = &node->power_uw.u64val; + + node->max_power_range_uw.filename = "max_power_range_uw"; + node->max_power_range_uw.fmt = "%llu"; + node->max_power_range_uw.ptr = &node->max_power_range_uw.u64val; + + for (i = 0; i < DTPM_CONSTRAINT_MAX; i++) { + node->constraints[i].name.filename = "constraint_0_name"; + node->constraints[i].name.fmt = "%s"; + node->constraints[i].name.ptr = node->constraints[i].name.name; + + node->constraints[i].power_limit_uw.filename = "constraint_0_power_limit_uw"; + node->constraints[i].power_limit_uw.fmt = "%llu"; + node->constraints[i].power_limit_uw.ptr = &node->constraints[i].power_limit_uw.u64val; + + node->constraints[i].time_window_us.filename = "constraint_0_time_window_us"; + node->constraints[i].time_window_us.fmt = "%llu"; + node->constraints[i].time_window_us.ptr = &node->constraints[i].time_window_us.u64val; + + node->constraints[i].max_power_uw.filename = "constraint_0_max_power_uw"; + node->constraints[i].max_power_uw.fmt = "%llu"; + node->constraints[i].max_power_uw.ptr = &node->constraints[i].max_power_uw.u64val; + } + + return node; +} + +void dtpm_node_free(struct dtpm_node *node) +{ + free(node); +} + +#define HASHMASK 0x00FF +#if 0 +static unsigned int hash(const char* key) +{ + unsigned int i; + unsigned int h; + + h = 1315423911; + for (i = 0; *key; key++, i++) { + h ^= ((h << 5) + (*key) + (h >> 2)); + } + h &= HASHMASK; + + return h; +} +#endif + +int for_each_dtpm_node(struct dtpm_node *node, dtpm_node_cb_t cb, void *data) +{ + int i, ret; + + ret = cb(node, data); + + for (i = 0; i < DTPM_CHILDREN_MAX; i++) { + + if (!node->children[i]) + continue; + + ret += for_each_dtpm_node(node->children[i], cb, data); + } + + return ret; +} + +static int dtpm_read_field(struct dtpm_node *node, struct dtpm_field *field) +{ + char path[PATH_MAX]; + + sprintf(path, "%s/%s", node->path, field->filename); + + return dtpm_file_rw(path, O_RDONLY, field->fmt, field->ptr); +} + +int dtpm_read(struct dtpm_node *node) +{ + int i; + int ret = 0; + + ret += dtpm_read_field(node, &node->max_power_range_uw); + ret += dtpm_read_field(node, &node->power_uw); + ret += dtpm_read_field(node, &node->name); + + for (i = 0; i < DTPM_CONSTRAINT_MAX; i++) { + ret += dtpm_read_field(node, &node->constraints[i].name); + ret += dtpm_read_field(node, &node->constraints[i].power_limit_uw); + ret += dtpm_read_field(node, &node->constraints[i].max_power_uw); + } + + return ret; +} + +int dtpm_init(const char *dirname, struct dtpm_node *node) +{ + node->path = strdup(dirname); + if (!node->path) { + fprintf(stderr, "Failed to strdup '%s': %m\n", dirname); + return -1; + } + + if (dtpm_read(node) < 0) { + fprintf(stderr, "Failed to read node information '%s'\n", dirname); + return -1; + } + + return 0; +} + +struct dtpm_node *dtpm_build_tree(const char *dirname, struct dtpm_node *parent) +{ + DIR *dir; + int ret = -1; + struct dirent *direntp; + struct dtpm_node *new_node; + + DEBUG("Building tree from '%s'\n", dirname); + + dir = opendir(dirname); + if (!dir) { + ERROR("Failed to open directory '%s': %m\n", dirname); + return NULL; + } + + new_node = dtpm_node_alloc(); + if (!new_node) { + ERROR("Failed to allocated node\n"); + goto out_close_dir; + } + + new_node-> parent = parent; + + ret = dtpm_init(dirname, new_node); + if (ret) + goto out_dtpm_node_free; + + while ((direntp = readdir(dir)) != NULL) { + + char *subdir; + struct dtpm_node *child; + + if (strncmp(direntp->d_name, "dtpm", strlen("dtpm"))) + continue; + + if (asprintf(&subdir, "%s/%s", dirname, direntp->d_name) == -1) { + fprintf(stderr, "Failed to allocate string: %m\n"); + goto out_close_dir; + } + + child = dtpm_build_tree(subdir, new_node); + + if (!child) { + fprintf(stderr, "Failed to build '%s': %m\n", subdir); + free(subdir); + goto out_dtpm_node_free; + } + + free(subdir); + + ret = dtpm_node_add_child(new_node, child); + if (ret) { + fprintf(stderr, "Failed to add new node\n"); + goto out_dtpm_node_free; + } + } + +out_close_dir: + closedir(dir); +out_dtpm_node_free: + if (ret) + dtpm_node_free(new_node); + + return ret ? NULL : new_node; +} + +int show_power_uw(struct dtpm_node *node, void *data) +{ + dtpm_read_field(node, &node->power_uw); + + printf("%s: %llu uW\n", node->name.name, node->power_uw.u64val); + + return 0; +} + +int main(void) +{ + struct dtpm_node *root; + char *config_path = NULL; + + if (getuid()) { + CRITICAL("Needs root privileges\n"); + return -1; + } + + if (dtpm_config(config_path)) { + FATAL("Failed to configure\n"); + return -1; + } + + root = dtpm_build_tree(dtpm_path(), NULL); + if (!root) + return -1; + + while (1) { + if (for_each_dtpm_node(root, show_power_uw, NULL)) + return -1; + + usleep(500000); + + printf("\n\n"); + } + + return 0; +} diff --git a/src/dtpm/dtpm.h b/src/dtpm/dtpm.h new file mode 100644 index 0000000..9e9bcd9 --- /dev/null +++ b/src/dtpm/dtpm.h @@ -0,0 +1,4 @@ +#ifndef __DTPM_H +#define __DTPM_H + +#endif diff --git a/src/dtpm/dtpm_conf.c b/src/dtpm/dtpm_conf.c new file mode 100644 index 0000000..cf3e23b --- /dev/null +++ b/src/dtpm/dtpm_conf.c @@ -0,0 +1,237 @@ +#define _GNU_SOURCE +#include <libconfig.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/types.h> +#include <unistd.h> +#include <dirent.h> +#include <mntent.h> +#include <pwd.h> + +#include "dtpm_log.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +#endif + +#define DTPM_CONFIG_PATH "dtpm" +#define DTPM_PATH "devices/virtual/powercap/dtpm/dtpm:0" + +static char *__dtpm_path; +static char *dtpm_config_path; + +static int dtpm_get_config(char *path) +{ + int i; + const char *home; + const char *home_path[] = { + ".config/dtpm/config", + ".dtpm/config", + }; + + const char *sys_path[] = { + "/etc/default/dtpm", + "/etc/dtpm.conf", + }; + + home = getenv("HOME"); + if (!home) { + struct passwd *pw = getpwuid(getuid()); + + if (!pw) { + ERROR("Failed to get the home directory: %m\n"); + return -1; + } + + home = pw->pw_dir; + } + + DEBUG("Found home directory: '%s'\n", home); + + for (i = 0; i < ARRAY_SIZE(home_path); i++) { + + sprintf(path, "%s/%s", home, home_path[i]); + + if (!access(path, F_OK)) { + NOTICE("Found configuration file '%s'\n", path); + return 0; + } + } + + for (i = 0; i < ARRAY_SIZE(sys_path); i++) { + + strcpy(path, sys_path[i]); + + if (!access(path, F_OK)) { + NOTICE("Found configuration file '%s'\n", path); + return 0; + } + } + + return -1; +} + +static int dtpm_create_constraints(config_t *cfg, const char *dtpm_conf_path, + config_setting_t *constraints) +{ + int i, length; + char *path; + + length = config_setting_length(constraints); + + for (i = 0; i < length; i++) { + + config_setting_t *node = config_setting_get_elem(constraints, i); + config_setting_t *child; + const char *descr, *name; + + if (!config_setting_lookup_string(node, "name", &name)) { + WARN("Name missing for entry '%d' for '%s'\n", + i, constraints->name); + continue; + } + + if (!config_setting_lookup_string(node, "description", &descr)) { + WARN("Constraint description missing for '%s'\n", name); + } + + NOTICE("Found constraint '%s' for '%s'\n", descr ? descr : "", name); + + asprintf(&path, "%s/%s", dtpm_conf_path, name); + + DEBUG("Creating directory '%s'\n", path); + + if (mkdir(path, 0700)) { + ERROR("Failed to create directory '%s': %m\n", path); + continue; + } + + child = config_setting_lookup(node, "constraints"); + if (child) + dtpm_create_constraints(cfg, path, child); + + free(path); + } + + return 0; +} + +static const char* get_mnt(const char *fsname) +{ + struct mntent *ent; + const char *fstab = "/proc/mounts"; + const char *mnt = NULL; + FILE *f; + + DEBUG("Getting '%s' mount point\n", fsname); + + f = setmntent(fstab, "r"); + if (!f) { + WARN("Failed to open '%s': %m\n", fstab); + return NULL; + } + + while ((ent = getmntent(f))) { + + if (strcmp(fsname, ent->mnt_fsname)) + continue; + + mnt = strdup(ent->mnt_dir); + if (!mnt) { + CRITICAL("Failed to allocate string\n"); + break; + } + + NOTICE("Found '%s' mount point at '%s'\n", fsname, mnt); + break; + } + + endmntent(f); + + return mnt; +} + +char *dtpm_path(void) +{ + return __dtpm_path; +} + +static int dtpm_init_path(void) +{ + const char *mnt; + + mnt = get_mnt("sysfs"); + if (!mnt) { + CRITICAL("No sysfs filesystem found\n"); + return -1; + } + + if (asprintf(&__dtpm_path, "%s/%s", mnt, DTPM_PATH) < 0) { + CRITICAL("Failed to build path to '%s'\n", mnt); + return -1; + } + + mnt = get_mnt("configfs"); + if (!mnt) { + WARN("No config file system found\n"); + } else { + if (asprintf(&dtpm_config_path, "%s/%s/constraints", mnt, DTPM_CONFIG_PATH) < 0) + WARN("Failed to allocate DTPM config path"); + } + + return 0; +} + +int dtpm_config(char *conf_path) +{ + config_t cfg; + config_setting_t *constraints; + const char *str; + + config_init(&cfg); + + if (dtpm_init_path()) { + ERROR("Failed to initialize the different sysfs path"); + return -1; + } + + if (!conf_path) { + conf_path = alloca(PATH_MAX); + if (dtpm_get_config(conf_path)) { + ERROR("No configuration file found\n"); + return -1; + } + } + + if (!config_read_file(&cfg, conf_path)) { + ERROR("Failed to parse %s:%d - %s\n", config_error_file(&cfg), + config_error_line(&cfg), config_error_text(&cfg)); + + return -1; + } + + if (!config_lookup_string(&cfg, "name", &str)) { + ERROR("Failed to retrieve the model name\n"); + return -1; + } + + NOTICE("Found model name: '%s'\n", str); + + if (!dtpm_config_path) { + INFO("No config file system found, discarding configuration\n"); + return 0; + } + + constraints = config_lookup(&cfg, "constraints"); + if (!constraints) { + ERROR("Failed to retrieve the constraints entry\n"); + return -1; + } + + return dtpm_create_constraints(&cfg, dtpm_config_path, constraints); +} diff --git a/src/dtpm/dtpm_conf.h b/src/dtpm/dtpm_conf.h new file mode 100644 index 0000000..08ede16 --- /dev/null +++ b/src/dtpm/dtpm_conf.h @@ -0,0 +1,9 @@ +#ifndef __DTPM_CONF_H +#define __DTPM_CONF_H + +char *dtpm_config_path(void); +char *dtpm_path(); + +int dtpm_config(char *conf_path); + +#endif diff --git a/src/dtpm/dtpm_log.h b/src/dtpm/dtpm_log.h new file mode 100644 index 0000000..98f59e9 --- /dev/null +++ b/src/dtpm/dtpm_log.h @@ -0,0 +1,13 @@ +#ifndef __DTPM_LOG_H +#define __DTPM_LOG_H + +#define DEBUG(fmt, ...) fprintf(stdout, "DEBUG %s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define INFO(fmt, ...) fprintf(stdout, "INFO: " fmt, ##__VA_ARGS__) +#define NOTICE(fmt, ...) fprintf(stdout, "NOTICE: " fmt, ##__VA_ARGS__) +#define WARN(fmt, ...) fprintf(stderr, "WARN: " fmt, ##__VA_ARGS__) +#define ERROR(fmt, ...) fprintf(stderr, "ERROR: " fmt, ##__VA_ARGS__) +#define CRITICAL(fmt, ...) fprintf(stderr, "CRITICAL: " fmt, ##__VA_ARGS__) +#define ALERT(fmt, ...) fprintf(stderr, "ALERT: " fmt, ##__VA_ARGS__) +#define FATAL(fmt, ...) fprintf(stderr, "FATAL: " fmt, ##__VA_ARGS__) + +#endif diff --git a/src/dtpm/log.h b/src/dtpm/log.h new file mode 100644 index 0000000..98f59e9 --- /dev/null +++ b/src/dtpm/log.h @@ -0,0 +1,13 @@ +#ifndef __DTPM_LOG_H +#define __DTPM_LOG_H + +#define DEBUG(fmt, ...) fprintf(stdout, "DEBUG %s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define INFO(fmt, ...) fprintf(stdout, "INFO: " fmt, ##__VA_ARGS__) +#define NOTICE(fmt, ...) fprintf(stdout, "NOTICE: " fmt, ##__VA_ARGS__) +#define WARN(fmt, ...) fprintf(stderr, "WARN: " fmt, ##__VA_ARGS__) +#define ERROR(fmt, ...) fprintf(stderr, "ERROR: " fmt, ##__VA_ARGS__) +#define CRITICAL(fmt, ...) fprintf(stderr, "CRITICAL: " fmt, ##__VA_ARGS__) +#define ALERT(fmt, ...) fprintf(stderr, "ALERT: " fmt, ##__VA_ARGS__) +#define FATAL(fmt, ...) fprintf(stderr, "FATAL: " fmt, ##__VA_ARGS__) + +#endif |