Loading Documentation/devicetree/bindings/scheduler/energy.txt 0 → 100644 +13 −0 Original line number Diff line number Diff line * Scheduler Energy Driver Scheduler Energy Driver updates capacities in the scheduler group energy array. The array contains power cost at each CPU operating points so energy aware scheduler (EAS) can utilize it for task placement. Required properties: - compatible: Must be "sched-energy" Example: energy-costs { compatible = "sched-energy"; } arch/arm64/include/asm/topology.h +3 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,9 @@ extern unsigned long cpufreq_scale_max_freq_capacity(int cpu); #define arch_scale_cpu_capacity scale_cpu_capacity extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu); #define arch_update_cpu_capacity update_cpu_power_capacity extern void update_cpu_power_capacity(int cpu); #include <asm-generic/topology.h> #endif /* _ASM_ARM_TOPOLOGY_H */ arch/arm64/kernel/topology.c +6 −2 Original line number Diff line number Diff line Loading @@ -449,6 +449,12 @@ static void update_cpu_capacity(unsigned int cpu) cpu, arch_scale_cpu_capacity(NULL, cpu)); } void update_cpu_power_capacity(int cpu) { update_cpu_power(cpu); update_cpu_capacity(cpu); } static void update_siblings_masks(unsigned int cpuid) { struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; Loading Loading @@ -510,8 +516,6 @@ void store_cpu_topology(unsigned int cpuid) topology_populated: update_siblings_masks(cpuid); update_cpu_power(cpuid); update_cpu_capacity(cpuid); } static void __init reset_cpu_topology(void) Loading include/linux/sched.h +3 −2 Original line number Diff line number Diff line Loading @@ -1155,8 +1155,9 @@ struct sched_domain_attr { extern int sched_domain_level_max; struct capacity_state { unsigned long cap; /* compute capacity */ unsigned long power; /* power consumption at this compute capacity */ unsigned long cap; /* capacity - calculated by energy driver */ unsigned long frequency;/* frequency */ unsigned long power; /* power consumption at this frequency */ }; struct idle_state { Loading kernel/sched/energy.c +155 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ #include <linux/sched.h> #include <linux/sched_energy.h> #include <linux/stddef.h> #include <linux/cpu.h> #include <linux/pm_opp.h> #include <linux/platform_device.h> struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS]; Loading Loading @@ -97,7 +100,8 @@ void init_sched_energy_costs(void) } for (i = 0, val = prop->value; i < nstates; i++) { cap_states[i].cap = be32_to_cpup(val++); cap_states[i].cap = SCHED_CAPACITY_SCALE; cap_states[i].frequency = be32_to_cpup(val++); cap_states[i].power = be32_to_cpup(val++); } Loading Loading @@ -138,3 +142,153 @@ void init_sched_energy_costs(void) out: free_resources(); } static int sched_energy_probe(struct platform_device *pdev) { unsigned long max_freq = 0; int max_efficiency = INT_MIN; int cpu; unsigned long *max_frequencies = NULL; int ret; if (!sched_is_energy_aware()) return 0; max_frequencies = kmalloc_array(nr_cpu_ids, sizeof(unsigned long), GFP_KERNEL); if (!max_frequencies) { ret = -ENOMEM; goto exit; } /* * Find system max possible frequency and max frequencies for each * CPUs. */ for_each_possible_cpu(cpu) { struct device *cpu_dev; struct dev_pm_opp *opp; int efficiency = arch_get_cpu_efficiency(cpu); max_efficiency = max(efficiency, max_efficiency); cpu_dev = get_cpu_device(cpu); if (IS_ERR_OR_NULL(cpu_dev)) { if (!cpu_dev) ret = -EINVAL; else ret = PTR_ERR(cpu_dev); goto exit; } max_frequencies[cpu] = ULONG_MAX; rcu_read_lock(); opp = dev_pm_opp_find_freq_floor(cpu_dev, &max_frequencies[cpu]); if (IS_ERR_OR_NULL(opp)) { if (!opp || PTR_ERR(opp) == -ENODEV) ret = -EPROBE_DEFER; else ret = PTR_ERR(opp); goto exit_rcu_unlock; } rcu_read_unlock(); /* Convert HZ to KHZ */ max_frequencies[cpu] /= 1000; max_freq = max(max_freq, max_frequencies[cpu]); } /* update capacity in energy model */ for_each_possible_cpu(cpu) { unsigned long cpu_max_cap; struct sched_group_energy *sge_l0, *sge; int efficiency = arch_get_cpu_efficiency(cpu); cpu_max_cap = DIV_ROUND_UP(SCHED_CAPACITY_SCALE * max_frequencies[cpu], max_freq); cpu_max_cap = DIV_ROUND_UP(cpu_max_cap * efficiency, max_efficiency); /* * All the cap_states have same frequency table so use * SD_LEVEL0's. */ sge_l0 = sge_array[cpu][SD_LEVEL0]; if (sge_l0 && sge_l0->nr_cap_states > 0) { int i; for (i = 0; i < sge_l0->nr_cap_states; i++) { int sd_level; unsigned long freq, cap; /* * Energy model can contain more frequency * steps than actual for multiple speedbin * support. Ceil the max capacity with actual * one. */ freq = min(sge_l0->cap_states[i].frequency, max_frequencies[cpu]); cap = DIV_ROUND_UP(cpu_max_cap * freq, max_frequencies[cpu]); for_each_possible_sd_level(sd_level) { sge = sge_array[cpu][sd_level]; if (!sge) break; sge->cap_states[i].cap = cap; } dev_dbg(&pdev->dev, "cpu=%d freq=%ld cap=%ld power_d0=%ld\n", cpu, freq, sge_l0->cap_states[i].cap, sge_l0->cap_states[i].power); } } dev_dbg(&pdev->dev, "cpu=%d efficiency=%d max_frequency=%ld max_efficiency=%d cpu_max_capacity=%ld\n", cpu, efficiency, max_frequencies[cpu], max_efficiency, cpu_max_cap); arch_update_cpu_capacity(cpu); } kfree(max_frequencies); dev_info(&pdev->dev, "Sched-energy-costs capacity updated\n"); return 0; exit_rcu_unlock: rcu_read_unlock(); exit: if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "error=%d\n", ret); kfree(max_frequencies); return ret; } static const struct of_device_id of_sched_energy_dt[] = { { .compatible = "sched-energy", }, { } }; static struct platform_driver energy_driver = { .driver = { .name = "sched-energy", .of_match_table = of_sched_energy_dt, }, .probe = sched_energy_probe, }; static int __init sched_energy_init(void) { return platform_driver_register(&energy_driver); } subsys_initcall(sched_energy_init); Loading
Documentation/devicetree/bindings/scheduler/energy.txt 0 → 100644 +13 −0 Original line number Diff line number Diff line * Scheduler Energy Driver Scheduler Energy Driver updates capacities in the scheduler group energy array. The array contains power cost at each CPU operating points so energy aware scheduler (EAS) can utilize it for task placement. Required properties: - compatible: Must be "sched-energy" Example: energy-costs { compatible = "sched-energy"; }
arch/arm64/include/asm/topology.h +3 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,9 @@ extern unsigned long cpufreq_scale_max_freq_capacity(int cpu); #define arch_scale_cpu_capacity scale_cpu_capacity extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu); #define arch_update_cpu_capacity update_cpu_power_capacity extern void update_cpu_power_capacity(int cpu); #include <asm-generic/topology.h> #endif /* _ASM_ARM_TOPOLOGY_H */
arch/arm64/kernel/topology.c +6 −2 Original line number Diff line number Diff line Loading @@ -449,6 +449,12 @@ static void update_cpu_capacity(unsigned int cpu) cpu, arch_scale_cpu_capacity(NULL, cpu)); } void update_cpu_power_capacity(int cpu) { update_cpu_power(cpu); update_cpu_capacity(cpu); } static void update_siblings_masks(unsigned int cpuid) { struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; Loading Loading @@ -510,8 +516,6 @@ void store_cpu_topology(unsigned int cpuid) topology_populated: update_siblings_masks(cpuid); update_cpu_power(cpuid); update_cpu_capacity(cpuid); } static void __init reset_cpu_topology(void) Loading
include/linux/sched.h +3 −2 Original line number Diff line number Diff line Loading @@ -1155,8 +1155,9 @@ struct sched_domain_attr { extern int sched_domain_level_max; struct capacity_state { unsigned long cap; /* compute capacity */ unsigned long power; /* power consumption at this compute capacity */ unsigned long cap; /* capacity - calculated by energy driver */ unsigned long frequency;/* frequency */ unsigned long power; /* power consumption at this frequency */ }; struct idle_state { Loading
kernel/sched/energy.c +155 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ #include <linux/sched.h> #include <linux/sched_energy.h> #include <linux/stddef.h> #include <linux/cpu.h> #include <linux/pm_opp.h> #include <linux/platform_device.h> struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS]; Loading Loading @@ -97,7 +100,8 @@ void init_sched_energy_costs(void) } for (i = 0, val = prop->value; i < nstates; i++) { cap_states[i].cap = be32_to_cpup(val++); cap_states[i].cap = SCHED_CAPACITY_SCALE; cap_states[i].frequency = be32_to_cpup(val++); cap_states[i].power = be32_to_cpup(val++); } Loading Loading @@ -138,3 +142,153 @@ void init_sched_energy_costs(void) out: free_resources(); } static int sched_energy_probe(struct platform_device *pdev) { unsigned long max_freq = 0; int max_efficiency = INT_MIN; int cpu; unsigned long *max_frequencies = NULL; int ret; if (!sched_is_energy_aware()) return 0; max_frequencies = kmalloc_array(nr_cpu_ids, sizeof(unsigned long), GFP_KERNEL); if (!max_frequencies) { ret = -ENOMEM; goto exit; } /* * Find system max possible frequency and max frequencies for each * CPUs. */ for_each_possible_cpu(cpu) { struct device *cpu_dev; struct dev_pm_opp *opp; int efficiency = arch_get_cpu_efficiency(cpu); max_efficiency = max(efficiency, max_efficiency); cpu_dev = get_cpu_device(cpu); if (IS_ERR_OR_NULL(cpu_dev)) { if (!cpu_dev) ret = -EINVAL; else ret = PTR_ERR(cpu_dev); goto exit; } max_frequencies[cpu] = ULONG_MAX; rcu_read_lock(); opp = dev_pm_opp_find_freq_floor(cpu_dev, &max_frequencies[cpu]); if (IS_ERR_OR_NULL(opp)) { if (!opp || PTR_ERR(opp) == -ENODEV) ret = -EPROBE_DEFER; else ret = PTR_ERR(opp); goto exit_rcu_unlock; } rcu_read_unlock(); /* Convert HZ to KHZ */ max_frequencies[cpu] /= 1000; max_freq = max(max_freq, max_frequencies[cpu]); } /* update capacity in energy model */ for_each_possible_cpu(cpu) { unsigned long cpu_max_cap; struct sched_group_energy *sge_l0, *sge; int efficiency = arch_get_cpu_efficiency(cpu); cpu_max_cap = DIV_ROUND_UP(SCHED_CAPACITY_SCALE * max_frequencies[cpu], max_freq); cpu_max_cap = DIV_ROUND_UP(cpu_max_cap * efficiency, max_efficiency); /* * All the cap_states have same frequency table so use * SD_LEVEL0's. */ sge_l0 = sge_array[cpu][SD_LEVEL0]; if (sge_l0 && sge_l0->nr_cap_states > 0) { int i; for (i = 0; i < sge_l0->nr_cap_states; i++) { int sd_level; unsigned long freq, cap; /* * Energy model can contain more frequency * steps than actual for multiple speedbin * support. Ceil the max capacity with actual * one. */ freq = min(sge_l0->cap_states[i].frequency, max_frequencies[cpu]); cap = DIV_ROUND_UP(cpu_max_cap * freq, max_frequencies[cpu]); for_each_possible_sd_level(sd_level) { sge = sge_array[cpu][sd_level]; if (!sge) break; sge->cap_states[i].cap = cap; } dev_dbg(&pdev->dev, "cpu=%d freq=%ld cap=%ld power_d0=%ld\n", cpu, freq, sge_l0->cap_states[i].cap, sge_l0->cap_states[i].power); } } dev_dbg(&pdev->dev, "cpu=%d efficiency=%d max_frequency=%ld max_efficiency=%d cpu_max_capacity=%ld\n", cpu, efficiency, max_frequencies[cpu], max_efficiency, cpu_max_cap); arch_update_cpu_capacity(cpu); } kfree(max_frequencies); dev_info(&pdev->dev, "Sched-energy-costs capacity updated\n"); return 0; exit_rcu_unlock: rcu_read_unlock(); exit: if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "error=%d\n", ret); kfree(max_frequencies); return ret; } static const struct of_device_id of_sched_energy_dt[] = { { .compatible = "sched-energy", }, { } }; static struct platform_driver energy_driver = { .driver = { .name = "sched-energy", .of_match_table = of_sched_energy_dt, }, .probe = sched_energy_probe, }; static int __init sched_energy_init(void) { return platform_driver_register(&energy_driver); } subsys_initcall(sched_energy_init);