summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lezcano <daniel.lezcano@linaro.org>2021-04-01 23:08:29 +0200
committerDaniel Lezcano <daniel.lezcano@linaro.org>2021-04-01 23:08:29 +0200
commitb47b7db0e0fdde6cd75f8e72d149af04c1ab48f1 (patch)
tree7e825722b718678fe8da0a45d1b56cb95d3eacc3
Initial import
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r--src/dtpm/Makefile34
-rw-r--r--src/dtpm/dtpm-rl.c536
-rw-r--r--src/dtpm/dtpm.c326
-rw-r--r--src/dtpm/dtpm.h4
-rw-r--r--src/dtpm/dtpm_conf.c237
-rw-r--r--src/dtpm/dtpm_conf.h9
-rw-r--r--src/dtpm/dtpm_log.h13
-rw-r--r--src/dtpm/log.h13
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