sched: ems: build simplified energy table
authorPark Bumgyu <bumgyu.park@samsung.com>
Thu, 5 Apr 2018 11:01:19 +0000 (20:01 +0900)
committerChungwoo Park <cww.park@samsung.com>
Mon, 21 May 2018 08:35:33 +0000 (17:35 +0900)
The simplified energy table only considers the compute capacity and power
of the cpu. When the frequency domain is registered, an energy table is
created dynamically.

Change-Id: Ifbd466dbd6ba0e496f98f17e3dd3b2850ab3ceea
Signed-off-by: Park Bumgyu <bumgyu.park@samsung.com>
drivers/cpufreq/exynos-acme.c
include/linux/ems.h
kernel/sched/ems/core.c

index a5af5b602c813138ac274044dc06971d772a460d..011950c1942f82afe4d7db7f21a765902af42dcb 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/pm_opp.h>
 #include <linux/cpu_cooling.h>
 #include <linux/suspend.h>
+#include <linux/ems.h>
 
 #include <soc/samsung/cal-if.h>
 #include <soc/samsung/exynos-dm.h>
@@ -1228,6 +1229,9 @@ static __init int init_table(struct exynos_cpufreq_domain *domain)
        domain->freq_table[index].driver_data = index;
        domain->freq_table[index].frequency = CPUFREQ_TABLE_END;
 
+       init_sched_energy_table(&domain->cpus, domain->table_size, table, volt_table,
+                               domain->max_freq, domain->min_freq);
+
        kfree(volt_table);
 
 free_table:
index d35dba1f38f6515ebda1115d3af146a11733a9b3..e09f91efa3316c49b256bb4a29b0b8dc4d061fd3 100644 (file)
@@ -61,6 +61,10 @@ extern void update_lbt_overutil(int cpu, unsigned long capacity);
 extern void gb_qos_update_request(struct gb_qos_request *req, u32 new_value);
 
 extern void request_kernel_prefer_perf(int grp_idx, int enable);
+
+extern void init_sched_energy_table(struct cpumask *cpus, int table_size,
+                               unsigned long *f_table, unsigned int *v_table,
+                               int max_f, int min_f);
 #else
 static inline int exynos_estimate_idle_state(int cpu_idx, struct cpumask *mask,
                                int state, int cpus) { return 0; }
@@ -92,4 +96,8 @@ static inline void update_lbt_overutil(int cpu, unsigned long capacity) { }
 static inline void gb_qos_update_request(struct gb_qos_request *req, u32 new_value) { }
 
 static inline void request_kernel_prefer_perf(int grp_idx, int enable) { }
+
+static inline void init_sched_energy_table(struct cpumask *cpus, int table_size,
+                               unsigned long *f_table, unsigned int *v_table,
+                               int max_f, int min_f) { }
 #endif /* CONFIG_SCHED_EMS */
index f75c9b2a96c094f6626af6f1419ee1608fbeedb8..f09b875a82cdf92f7b22587b8267119ddb6cd315 100644 (file)
@@ -32,15 +32,36 @@ static int cpu_util_wake(int cpu, struct task_struct *p)
        return (util >= capacity) ? capacity : util;
 }
 
+/*
+ * The compute capacity, power consumption at this compute capacity and
+ * frequency of state. The cap and power are used to find the energy
+ * efficiency cpu, and the frequency is used to create the capacity table.
+ */
+struct energy_state {
+       unsigned long cap;
+       unsigned long power;
+       unsigned long frequency;
+};
+
+/*
+ * Each cpu can have its own mips, coefficient and energy table. Generally,
+ * cpus in the same frequency domain have the same mips, coefficient and
+ * energy table.
+ */
 struct energy_table {
        unsigned int mips;
        unsigned int coefficient;;
-       struct capacity_state *states;
+
+       struct energy_state *states;
        unsigned int nr_states;
 };
-
 DEFINE_PER_CPU(struct energy_table, energy_table);
 
+/*
+ * When choosing cpu considering energy efficiency, decide best cpu and
+ * backup cpu according to policy, and then choose cpu which consumes the
+ * least energy including prev cpu.
+ */
 struct eco_env {
        struct task_struct *p;
 
@@ -182,6 +203,157 @@ static int __init init_sched_energy_data(void)
 }
 pure_initcall(init_sched_energy_data);
 
+static void
+fill_power_table(struct energy_table *table, int table_size,
+                       unsigned long *f_table, unsigned int *v_table,
+                       int max_f, int min_f)
+{
+       int i, index = 0;
+       int c = table->coefficient, v;
+       unsigned long f, power;
+
+       /* energy table and frequency table are inverted */
+       for (i = table_size - 1; i >= 0; i--) {
+               if (f_table[i] > max_f || f_table[i] < min_f)
+                       continue;
+
+               f = f_table[i] / 1000;  /* KHz -> MHz */
+               v = v_table[i] / 1000;  /* uV -> mV */
+
+               /*
+                * power = coefficent * frequency * voltage^2
+                */
+               power = c * f * v * v;
+
+               /*
+                * Generally, frequency is more than treble figures in MHz and
+                * voltage is also more then treble figures in mV, so the
+                * calculated power is larger than 10^9. For convenience of
+                * calculation, divide the value by 10^9.
+                */
+               do_div(power, 1000000000);
+               table->states[index].power = power;
+
+               /* save frequency to energy table */
+               table->states[index].frequency = f_table[i];
+               index++;
+       }
+}
+
+static void
+fill_cap_table(struct energy_table *table, int max_mips, unsigned long max_mips_freq)
+{
+       int i, m = table->mips;
+       unsigned long f;
+
+       for (i = 0; i < table->nr_states; i++) {
+               f = table->states[i].frequency;
+
+               /*
+                * capacity = freq/max_freq * mips/max_mips * 1024
+                */
+               table->states[i].cap = f * m * 1024 / max_mips_freq / max_mips;
+       }
+}
+
+static void show_energy_table(struct energy_table *table, int cpu)
+{
+       int i;
+
+       pr_info("[Energy Table : cpu%d]\n", cpu);
+       for (i = 0; i < table->nr_states; i++) {
+               pr_info("[%d] .cap=%lu .power=%lu\n", i,
+                       table->states[i].cap, table->states[i].power);
+       }
+}
+
+/*
+ * Whenever frequency domain is registered, and energy table corresponding to
+ * the domain is created. Because cpu in the same frequency domain has the same
+ * energy table. Capacity is calculated based on the max frequency of the fastest
+ * cpu, so once the frequency domain of the faster cpu is regsitered, capacity
+ * is recomputed.
+ */
+void init_sched_energy_table(struct cpumask *cpus, int table_size,
+                               unsigned long *f_table, unsigned int *v_table,
+                               int max_f, int min_f)
+{
+       struct energy_table *table;
+       int cpu, i, mips, valid_table_size = 0;
+       int max_mips = 0;
+       unsigned long max_mips_freq = 0;
+
+       mips = per_cpu(energy_table, cpumask_any(cpus)).mips;
+       for_each_cpu(cpu, cpus) {
+               /*
+                * All cpus in a frequency domain must have the smae capacity.
+                * Otherwise, it does not create an energy table because it
+                * is likely to be a human error.
+                */
+               if (mips != per_cpu(energy_table, cpu).mips) {
+                       pr_warn("cpu%d has different cpacity!!\n", cpu);
+                       return;
+               }
+       }
+
+       /* get size of valid frequency table to allocate energy table */
+       for (i = 0; i < table_size; i++) {
+               if (f_table[i] > max_f || f_table[i] < min_f)
+                       continue;
+
+               valid_table_size++;
+       }
+
+       /* there is no valid row in the table, energy table is not created */
+       if (!valid_table_size)
+               return;
+
+       /* allocate memory for energy table and fill power table */
+       for_each_cpu(cpu, cpus) {
+               table = &per_cpu(energy_table, cpu);
+               table->states = kcalloc(valid_table_size,
+                                       sizeof(struct energy_state), GFP_KERNEL);
+               if (unlikely(!table->states))
+                       return;
+
+               table->nr_states = valid_table_size;
+               fill_power_table(table, table_size, f_table, v_table, max_f, min_f);
+       }
+
+       /*
+        * Find fastest cpu among the cpu to which the energy table is allocated.
+        * The mips and max frequency of fastest cpu are needed to calculate
+        * capacity.
+        */
+       for_each_possible_cpu(cpu) {
+               table = &per_cpu(energy_table, cpu);
+               if (!table->states)
+                       continue;
+
+               if (table->mips > max_mips) {
+                       int last_state = table->nr_states - 1;
+
+                       max_mips = table->mips;
+                       max_mips_freq = table->states[last_state].frequency;
+               }
+       }
+
+       /*
+        * Calculate and fill capacity table.
+        * Recalculate the capacity whenever frequency domain changes because
+        * the fastest cpu may have changed and the capacity needs to be
+        * recalculated.
+        */
+       for_each_possible_cpu(cpu) {
+               table = &per_cpu(energy_table, cpu);
+               if (!table->states)
+                       continue;
+
+               fill_cap_table(table, max_mips, max_mips_freq);
+               show_energy_table(table, cpu);
+       }
+}
+
 static unsigned int calculate_energy(struct task_struct *p, int target_cpu)
 {
        unsigned long util[NR_CPUS] = {0, };