sched: Support for extracting EAS energy costs from DT
authorRobin Randhawa <robin.randhawa@arm.com>
Mon, 29 Jun 2015 17:01:58 +0000 (18:01 +0100)
committerLeo Yan <leo.yan@linaro.org>
Tue, 10 May 2016 08:49:53 +0000 (16:49 +0800)
This patch implements support for extracting energy cost data from DT.
The data should conform to the DT bindings for energy cost data needed
by EAS (energy aware scheduling).

Signed-off-by: Robin Randhawa <robin.randhawa@arm.com>
include/linux/sched_energy.h [new file with mode: 0644]
kernel/sched/Makefile
kernel/sched/energy.c [new file with mode: 0644]

diff --git a/include/linux/sched_energy.h b/include/linux/sched_energy.h
new file mode 100644 (file)
index 0000000..a3f1627
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _LINUX_SCHED_ENERGY_H
+#define _LINUX_SCHED_ENERGY_H
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+/*
+ * There doesn't seem to be an NR_CPUS style max number of sched domain
+ * levels so here's an arbitrary constant one for the moment.
+ *
+ * The levels alluded to here correspond to entries in struct
+ * sched_domain_topology_level that are meant to be populated by arch
+ * specific code (topology.c).
+ */
+#define NR_SD_LEVELS 8
+
+#define SD_LEVEL0   0
+#define SD_LEVEL1   1
+#define SD_LEVEL2   2
+#define SD_LEVEL3   3
+#define SD_LEVEL4   4
+#define SD_LEVEL5   5
+#define SD_LEVEL6   6
+#define SD_LEVEL7   7
+
+/*
+ * Convenience macro for iterating through said sd levels.
+ */
+#define for_each_possible_sd_level(level)                  \
+       for (level = 0; level < NR_SD_LEVELS; level++)
+
+extern struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];
+
+void init_sched_energy_costs(void);
+
+#endif
index 67687973ce80d63d3f52698fb4b738b76964b896..a541b5ce1dccb6e3f24b79d8e89ff01110d2933e 100644 (file)
@@ -12,7 +12,7 @@ CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer
 endif
 
 obj-y += core.o loadavg.o clock.o cputime.o
-obj-y += idle_task.o fair.o rt.o deadline.o stop_task.o
+obj-y += idle_task.o fair.o rt.o deadline.o stop_task.o energy.o
 obj-y += wait.o completion.o idle.o
 obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o
 obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
diff --git a/kernel/sched/energy.c b/kernel/sched/energy.c
new file mode 100644 (file)
index 0000000..b0656b7
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Obtain energy cost data from DT and populate relevant scheduler data
+ * structures.
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#define pr_fmt(fmt) "sched-energy: " fmt
+
+#define DEBUG
+
+#include <linux/gfp.h>
+#include <linux/of.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+#include <linux/sched_energy.h>
+#include <linux/stddef.h>
+
+struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];
+
+static void free_resources(void)
+{
+       int cpu, sd_level;
+       struct sched_group_energy *sge;
+
+       for_each_possible_cpu(cpu) {
+               for_each_possible_sd_level(sd_level) {
+                       sge = sge_array[cpu][sd_level];
+                       if (sge) {
+                               kfree(sge->cap_states);
+                               kfree(sge->idle_states);
+                               kfree(sge);
+                       }
+               }
+       }
+}
+
+void init_sched_energy_costs(void)
+{
+       struct device_node *cn, *cp;
+       struct capacity_state *cap_states;
+       struct idle_state *idle_states;
+       struct sched_group_energy *sge;
+       const struct property *prop;
+       int sd_level, i, nstates, cpu;
+       const __be32 *val;
+
+       for_each_possible_cpu(cpu) {
+               cn = of_get_cpu_node(cpu, NULL);
+               if (!cn) {
+                       pr_warn("CPU device node missing for CPU %d\n", cpu);
+                       return;
+               }
+
+               if (!of_find_property(cn, "sched-energy-costs", NULL)) {
+                       pr_warn("CPU device node has no sched-energy-costs\n");
+                       return;
+               }
+
+               for_each_possible_sd_level(sd_level) {
+                       cp = of_parse_phandle(cn, "sched-energy-costs", sd_level);
+                       if (!cp)
+                               break;
+
+                       prop = of_find_property(cp, "busy-cost-data", NULL);
+                       if (!prop || !prop->value) {
+                               pr_warn("No busy-cost data, skipping sched_energy init\n");
+                               goto out;
+                       }
+
+                       sge = kcalloc(1, sizeof(struct sched_group_energy),
+                                     GFP_NOWAIT);
+
+                       nstates = (prop->length / sizeof(u32)) / 2;
+                       cap_states = kcalloc(nstates,
+                                            sizeof(struct capacity_state),
+                                            GFP_NOWAIT);
+
+                       for (i = 0, val = prop->value; i < nstates; i++) {
+                               cap_states[i].cap = be32_to_cpup(val++);
+                               cap_states[i].power = be32_to_cpup(val++);
+                       }
+
+                       sge->nr_cap_states = nstates;
+                       sge->cap_states = cap_states;
+
+                       prop = of_find_property(cp, "idle-cost-data", NULL);
+                       if (!prop || !prop->value) {
+                               pr_warn("No idle-cost data, skipping sched_energy init\n");
+                               goto out;
+                       }
+
+                       nstates = (prop->length / sizeof(u32));
+                       idle_states = kcalloc(nstates,
+                                             sizeof(struct idle_state),
+                                             GFP_NOWAIT);
+
+                       for (i = 0, val = prop->value; i < nstates; i++)
+                               idle_states[i].power = be32_to_cpup(val++);
+
+                       sge->nr_idle_states = nstates;
+                       sge->idle_states = idle_states;
+
+                       sge_array[cpu][sd_level] = sge;
+               }
+       }
+
+       pr_info("Sched-energy-costs installed from DT\n");
+       return;
+
+out:
+       free_resources();
+}