cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dtpm_cpu.c (7648B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright 2020 Linaro Limited
      4 *
      5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
      6 *
      7 * The DTPM CPU is based on the energy model. It hooks the CPU in the
      8 * DTPM tree which in turns update the power number by propagating the
      9 * power number from the CPU energy model information to the parents.
     10 *
     11 * The association between the power and the performance state, allows
     12 * to set the power of the CPU at the OPP granularity.
     13 *
     14 * The CPU hotplug is supported and the power numbers will be updated
     15 * if a CPU is hot plugged / unplugged.
     16 */
     17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     18
     19#include <linux/cpumask.h>
     20#include <linux/cpufreq.h>
     21#include <linux/cpuhotplug.h>
     22#include <linux/dtpm.h>
     23#include <linux/energy_model.h>
     24#include <linux/of.h>
     25#include <linux/pm_qos.h>
     26#include <linux/slab.h>
     27#include <linux/units.h>
     28
     29struct dtpm_cpu {
     30	struct dtpm dtpm;
     31	struct freq_qos_request qos_req;
     32	int cpu;
     33};
     34
     35static DEFINE_PER_CPU(struct dtpm_cpu *, dtpm_per_cpu);
     36
     37static struct dtpm_cpu *to_dtpm_cpu(struct dtpm *dtpm)
     38{
     39	return container_of(dtpm, struct dtpm_cpu, dtpm);
     40}
     41
     42static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
     43{
     44	struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm);
     45	struct em_perf_domain *pd = em_cpu_get(dtpm_cpu->cpu);
     46	struct cpumask cpus;
     47	unsigned long freq;
     48	u64 power;
     49	int i, nr_cpus;
     50
     51	cpumask_and(&cpus, cpu_online_mask, to_cpumask(pd->cpus));
     52	nr_cpus = cpumask_weight(&cpus);
     53
     54	for (i = 0; i < pd->nr_perf_states; i++) {
     55
     56		power = pd->table[i].power * MICROWATT_PER_MILLIWATT * nr_cpus;
     57
     58		if (power > power_limit)
     59			break;
     60	}
     61
     62	freq = pd->table[i - 1].frequency;
     63
     64	freq_qos_update_request(&dtpm_cpu->qos_req, freq);
     65
     66	power_limit = pd->table[i - 1].power *
     67		MICROWATT_PER_MILLIWATT * nr_cpus;
     68
     69	return power_limit;
     70}
     71
     72static u64 scale_pd_power_uw(struct cpumask *pd_mask, u64 power)
     73{
     74	unsigned long max = 0, sum_util = 0;
     75	int cpu;
     76
     77	for_each_cpu_and(cpu, pd_mask, cpu_online_mask) {
     78
     79		/*
     80		 * The capacity is the same for all CPUs belonging to
     81		 * the same perf domain, so a single call to
     82		 * arch_scale_cpu_capacity() is enough. However, we
     83		 * need the CPU parameter to be initialized by the
     84		 * loop, so the call ends up in this block.
     85		 *
     86		 * We can initialize 'max' with a cpumask_first() call
     87		 * before the loop but the bits computation is not
     88		 * worth given the arch_scale_cpu_capacity() just
     89		 * returns a value where the resulting assembly code
     90		 * will be optimized by the compiler.
     91		 */
     92		max = arch_scale_cpu_capacity(cpu);
     93		sum_util += sched_cpu_util(cpu, max);
     94	}
     95
     96	/*
     97	 * In the improbable case where all the CPUs of the perf
     98	 * domain are offline, 'max' will be zero and will lead to an
     99	 * illegal operation with a zero division.
    100	 */
    101	return max ? (power * ((sum_util << 10) / max)) >> 10 : 0;
    102}
    103
    104static u64 get_pd_power_uw(struct dtpm *dtpm)
    105{
    106	struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm);
    107	struct em_perf_domain *pd;
    108	struct cpumask *pd_mask;
    109	unsigned long freq;
    110	int i;
    111
    112	pd = em_cpu_get(dtpm_cpu->cpu);
    113
    114	pd_mask = em_span_cpus(pd);
    115
    116	freq = cpufreq_quick_get(dtpm_cpu->cpu);
    117
    118	for (i = 0; i < pd->nr_perf_states; i++) {
    119
    120		if (pd->table[i].frequency < freq)
    121			continue;
    122
    123		return scale_pd_power_uw(pd_mask, pd->table[i].power *
    124					 MICROWATT_PER_MILLIWATT);
    125	}
    126
    127	return 0;
    128}
    129
    130static int update_pd_power_uw(struct dtpm *dtpm)
    131{
    132	struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm);
    133	struct em_perf_domain *em = em_cpu_get(dtpm_cpu->cpu);
    134	struct cpumask cpus;
    135	int nr_cpus;
    136
    137	cpumask_and(&cpus, cpu_online_mask, to_cpumask(em->cpus));
    138	nr_cpus = cpumask_weight(&cpus);
    139
    140	dtpm->power_min = em->table[0].power;
    141	dtpm->power_min *= MICROWATT_PER_MILLIWATT;
    142	dtpm->power_min *= nr_cpus;
    143
    144	dtpm->power_max = em->table[em->nr_perf_states - 1].power;
    145	dtpm->power_max *= MICROWATT_PER_MILLIWATT;
    146	dtpm->power_max *= nr_cpus;
    147
    148	return 0;
    149}
    150
    151static void pd_release(struct dtpm *dtpm)
    152{
    153	struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm);
    154	struct cpufreq_policy *policy;
    155
    156	if (freq_qos_request_active(&dtpm_cpu->qos_req))
    157		freq_qos_remove_request(&dtpm_cpu->qos_req);
    158
    159	policy = cpufreq_cpu_get(dtpm_cpu->cpu);
    160	if (policy) {
    161		for_each_cpu(dtpm_cpu->cpu, policy->related_cpus)
    162			per_cpu(dtpm_per_cpu, dtpm_cpu->cpu) = NULL;
    163	}
    164	
    165	kfree(dtpm_cpu);
    166}
    167
    168static struct dtpm_ops dtpm_ops = {
    169	.set_power_uw	 = set_pd_power_limit,
    170	.get_power_uw	 = get_pd_power_uw,
    171	.update_power_uw = update_pd_power_uw,
    172	.release	 = pd_release,
    173};
    174
    175static int cpuhp_dtpm_cpu_offline(unsigned int cpu)
    176{
    177	struct dtpm_cpu *dtpm_cpu;
    178
    179	dtpm_cpu = per_cpu(dtpm_per_cpu, cpu);
    180	if (dtpm_cpu)
    181		dtpm_update_power(&dtpm_cpu->dtpm);
    182
    183	return 0;
    184}
    185
    186static int cpuhp_dtpm_cpu_online(unsigned int cpu)
    187{
    188	struct dtpm_cpu *dtpm_cpu;
    189
    190	dtpm_cpu = per_cpu(dtpm_per_cpu, cpu);
    191	if (dtpm_cpu)
    192		return dtpm_update_power(&dtpm_cpu->dtpm);
    193
    194	return 0;
    195}
    196
    197static int __dtpm_cpu_setup(int cpu, struct dtpm *parent)
    198{
    199	struct dtpm_cpu *dtpm_cpu;
    200	struct cpufreq_policy *policy;
    201	struct em_perf_domain *pd;
    202	char name[CPUFREQ_NAME_LEN];
    203	int ret = -ENOMEM;
    204
    205	dtpm_cpu = per_cpu(dtpm_per_cpu, cpu);
    206	if (dtpm_cpu)
    207		return 0;
    208
    209	policy = cpufreq_cpu_get(cpu);
    210	if (!policy)
    211		return 0;
    212
    213	pd = em_cpu_get(cpu);
    214	if (!pd || em_is_artificial(pd))
    215		return -EINVAL;
    216
    217	dtpm_cpu = kzalloc(sizeof(*dtpm_cpu), GFP_KERNEL);
    218	if (!dtpm_cpu)
    219		return -ENOMEM;
    220
    221	dtpm_init(&dtpm_cpu->dtpm, &dtpm_ops);
    222	dtpm_cpu->cpu = cpu;
    223
    224	for_each_cpu(cpu, policy->related_cpus)
    225		per_cpu(dtpm_per_cpu, cpu) = dtpm_cpu;
    226
    227	snprintf(name, sizeof(name), "cpu%d-cpufreq", dtpm_cpu->cpu);
    228
    229	ret = dtpm_register(name, &dtpm_cpu->dtpm, parent);
    230	if (ret)
    231		goto out_kfree_dtpm_cpu;
    232
    233	ret = freq_qos_add_request(&policy->constraints,
    234				   &dtpm_cpu->qos_req, FREQ_QOS_MAX,
    235				   pd->table[pd->nr_perf_states - 1].frequency);
    236	if (ret)
    237		goto out_dtpm_unregister;
    238
    239	return 0;
    240
    241out_dtpm_unregister:
    242	dtpm_unregister(&dtpm_cpu->dtpm);
    243	dtpm_cpu = NULL;
    244
    245out_kfree_dtpm_cpu:
    246	for_each_cpu(cpu, policy->related_cpus)
    247		per_cpu(dtpm_per_cpu, cpu) = NULL;
    248	kfree(dtpm_cpu);
    249
    250	return ret;
    251}
    252
    253static int dtpm_cpu_setup(struct dtpm *dtpm, struct device_node *np)
    254{
    255	int cpu;
    256
    257	cpu = of_cpu_node_to_id(np);
    258	if (cpu < 0)
    259		return 0;
    260
    261	return __dtpm_cpu_setup(cpu, dtpm);
    262}
    263
    264static int dtpm_cpu_init(void)
    265{
    266	int ret;
    267
    268	/*
    269	 * The callbacks at CPU hotplug time are calling
    270	 * dtpm_update_power() which in turns calls update_pd_power().
    271	 *
    272	 * The function update_pd_power() uses the online mask to
    273	 * figure out the power consumption limits.
    274	 *
    275	 * At CPUHP_AP_ONLINE_DYN, the CPU is present in the CPU
    276	 * online mask when the cpuhp_dtpm_cpu_online function is
    277	 * called, but the CPU is still in the online mask for the
    278	 * tear down callback. So the power can not be updated when
    279	 * the CPU is unplugged.
    280	 *
    281	 * At CPUHP_AP_DTPM_CPU_DEAD, the situation is the opposite as
    282	 * above. The CPU online mask is not up to date when the CPU
    283	 * is plugged in.
    284	 *
    285	 * For this reason, we need to call the online and offline
    286	 * callbacks at different moments when the CPU online mask is
    287	 * consistent with the power numbers we want to update.
    288	 */
    289	ret = cpuhp_setup_state(CPUHP_AP_DTPM_CPU_DEAD, "dtpm_cpu:offline",
    290				NULL, cpuhp_dtpm_cpu_offline);
    291	if (ret < 0)
    292		return ret;
    293
    294	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "dtpm_cpu:online",
    295				cpuhp_dtpm_cpu_online, NULL);
    296	if (ret < 0)
    297		return ret;
    298
    299	return 0;
    300}
    301
    302static void dtpm_cpu_exit(void)
    303{
    304	cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN);
    305	cpuhp_remove_state_nocalls(CPUHP_AP_DTPM_CPU_DEAD);
    306}
    307
    308struct dtpm_subsys_ops dtpm_cpu_ops = {
    309	.name = KBUILD_MODNAME,
    310	.init = dtpm_cpu_init,
    311	.exit = dtpm_cpu_exit,
    312	.setup = dtpm_cpu_setup,
    313};