Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8b8b4dc authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "sched: energy: calculate and update CPU capacity dynamically"

parents 205041be 28c5121d
Loading
Loading
Loading
Loading
+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";
	}
+3 −0
Original line number Diff line number Diff line
@@ -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 */
+6 −2
Original line number Diff line number Diff line
@@ -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];
@@ -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)
+3 −2
Original line number Diff line number Diff line
@@ -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 {
+155 −1
Original line number Diff line number Diff line
@@ -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];

@@ -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++);
			}

@@ -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