summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lezcano <daniel.lezcano@linaro.org>2020-07-20 16:52:57 +0200
committerDaniel Lezcano <daniel.lezcano@linaro.org>2020-07-20 16:52:57 +0200
commitbf64bbf588ef9ee23fcde8fa5b5a9942d2d40b35 (patch)
treeaee694128e19f57eadfbfaffc1f6a84a6c33dc2f
Initial importHEADmaster
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r--Makefile2
-rw-r--r--powerem.c518
2 files changed, 520 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a4ada7b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+all:
+ gcc -g -Wall powerem.c -o powerem -lreadline
diff --git a/powerem.c b/powerem.c
new file mode 100644
index 0000000..6971b3a
--- /dev/null
+++ b/powerem.c
@@ -0,0 +1,518 @@
+/*
+ * 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 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 = (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 +=
+ (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 = (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(".powerem_history");
+
+ for (;;) {
+
+ int i = 0;
+
+ buf = readline("powerem:$ ");
+ 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(".powerem_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;
+}