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

Commit a8837753 authored by Joonwoo Park's avatar Joonwoo Park Committed by Gerrit - the friendly Code Review server
Browse files

sched: energy: calculate and update CPU capacity dynamically



One SoC can have multiple CPU speedbins which cannot be represented
with current energy model due to fixed capacity per CPU frequency
steps.

Provide CPU's all possible frequency steps instead of capacities along
with corresponding energy costs to be able to support different
speedbins.

Signed-off-by: default avatarJoonwoo Park <joonwoop@codeaurora.org>
[markivx: Resolved merge-conflicts]
Signed-off-by: default avatarVikram Mulukutla <markivx@codeaurora.org>

Change-Id: Ib865fd5ca7347946cfa532ca464dba4f9c51ced5
[satyap@codeaurora.org:
1. resolve trivial merge conflicts
2. port update_cpu_capacity function from msm-4.9 commit: 5e900d488428a6a
(ANDROID: arm64: Cpu invariant scheduler load-tracking and capacity support)
]
Signed-off-by: default avatarSatya Durga Srinivasu Prabhala <satyap@codeaurora.org>
parent cd35a9bc
Loading
Loading
Loading
Loading
+13 −0
Original line number Original line 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 Original line Diff line number Diff line
@@ -44,6 +44,9 @@ int pcibus_to_node(struct pci_bus *bus);
/* Enable topology flag updates */
/* Enable topology flag updates */
#define arch_update_cpu_topology topology_update_cpu_topology
#define arch_update_cpu_topology topology_update_cpu_topology


#define arch_update_cpu_capacity update_cpu_power_capacity
extern void update_cpu_power_capacity(int cpu);

#include <asm-generic/topology.h>
#include <asm-generic/topology.h>


#endif /* _ASM_ARM_TOPOLOGY_H */
#endif /* _ASM_ARM_TOPOLOGY_H */
+21 −0
Original line number Original line Diff line number Diff line
@@ -354,6 +354,27 @@ const struct sched_group_energy * const cpu_system_energy(int cpu)
	return sge;
	return sge;
}
}


static void update_cpu_capacity(unsigned int cpu)
{
	unsigned long capacity = SCHED_CAPACITY_SCALE;

	if (cpu_core_energy(cpu)) {
		int max_cap_idx = cpu_core_energy(cpu)->nr_cap_states - 1;

		capacity = cpu_core_energy(cpu)->cap_states[max_cap_idx].cap;
	}

	topology_set_cpu_scale(cpu, capacity);

	pr_info("CPU%d: update cpu_capacity %lu\n",
		cpu, arch_scale_cpu_capacity(NULL, cpu));
}

void update_cpu_power_capacity(int cpu)
{
	update_cpu_capacity(cpu);
}

static struct sched_domain_topology_level arm64_topology[] = {
static struct sched_domain_topology_level arm64_topology[] = {
#ifdef CONFIG_SCHED_SMT
#ifdef CONFIG_SCHED_SMT
	{ cpu_smt_mask, smt_flags, SD_INIT_NAME(SMT) },
	{ cpu_smt_mask, smt_flags, SD_INIT_NAME(SMT) },
+3 −2
Original line number Original line Diff line number Diff line
@@ -68,8 +68,9 @@ struct sched_domain_attr {
extern int sched_domain_level_max;
extern int sched_domain_level_max;


struct capacity_state {
struct capacity_state {
	unsigned long cap;	/* compute capacity */
	unsigned long cap;	/* capacity - calculated by energy driver */
	unsigned long power;	/* power consumption at this compute capacity */
	unsigned long frequency;/* frequency */
	unsigned long power;	/* power consumption at this frequency */
};
};


struct idle_state {
struct idle_state {
+155 −1
Original line number Original line Diff line number Diff line
@@ -28,6 +28,9 @@
#include <linux/sched_energy.h>
#include <linux/sched_energy.h>
#include <linux/stddef.h>
#include <linux/stddef.h>
#include <linux/arch_topology.h>
#include <linux/arch_topology.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];
struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];


@@ -105,7 +108,8 @@ void init_sched_energy_costs(void)
					     GFP_NOWAIT);
					     GFP_NOWAIT);


			for (i = 0, val = prop->value; i < nstates; i++) {
			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++);
				cap_states[i].power = be32_to_cpup(val++);
			}
			}


@@ -143,3 +147,153 @@ void init_sched_energy_costs(void)
out:
out:
	free_resources();
	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 = cpu > 3 ? 1740 : 1024;

		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 = cpu > 3 ? 1740 : 1024;

		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