1
2
3
4
5
6
7
8
9#define pr_fmt(fmt) "energy_model: " fmt
10
11#include <linux/cpu.h>
12#include <linux/cpumask.h>
13#include <linux/debugfs.h>
14#include <linux/energy_model.h>
15#include <linux/sched/topology.h>
16#include <linux/slab.h>
17
18
19static DEFINE_PER_CPU(struct em_perf_domain *, em_data);
20
21
22
23
24
25static DEFINE_MUTEX(em_pd_mutex);
26
27#ifdef CONFIG_DEBUG_FS
28static struct dentry *rootdir;
29
30static void em_debug_create_cs(struct em_cap_state *cs, struct dentry *pd)
31{
32 struct dentry *d;
33 char name[24];
34
35 snprintf(name, sizeof(name), "cs:%lu", cs->frequency);
36
37
38 d = debugfs_create_dir(name, pd);
39 debugfs_create_ulong("frequency", 0444, d, &cs->frequency);
40 debugfs_create_ulong("power", 0444, d, &cs->power);
41 debugfs_create_ulong("cost", 0444, d, &cs->cost);
42}
43
44static int em_debug_cpus_show(struct seq_file *s, void *unused)
45{
46 seq_printf(s, "%*pbl\n", cpumask_pr_args(to_cpumask(s->private)));
47
48 return 0;
49}
50DEFINE_SHOW_ATTRIBUTE(em_debug_cpus);
51
52static void em_debug_create_pd(struct em_perf_domain *pd, int cpu)
53{
54 struct dentry *d;
55 char name[8];
56 int i;
57
58 snprintf(name, sizeof(name), "pd%d", cpu);
59
60
61 d = debugfs_create_dir(name, rootdir);
62
63 debugfs_create_file("cpus", 0444, d, pd->cpus, &em_debug_cpus_fops);
64
65
66 for (i = 0; i < pd->nr_cap_states; i++)
67 em_debug_create_cs(&pd->table[i], d);
68}
69
70static int __init em_debug_init(void)
71{
72
73 rootdir = debugfs_create_dir("energy_model", NULL);
74
75 return 0;
76}
77core_initcall(em_debug_init);
78#else
79static void em_debug_create_pd(struct em_perf_domain *pd, int cpu) {}
80#endif
81static struct em_perf_domain *em_create_pd(cpumask_t *span, int nr_states,
82 struct em_data_callback *cb)
83{
84 unsigned long opp_eff, prev_opp_eff = ULONG_MAX;
85 unsigned long power, freq, prev_freq = 0;
86 int i, ret, cpu = cpumask_first(span);
87 struct em_cap_state *table;
88 struct em_perf_domain *pd;
89 u64 fmax;
90
91 if (!cb->active_power)
92 return NULL;
93
94 pd = kzalloc(sizeof(*pd) + cpumask_size(), GFP_KERNEL);
95 if (!pd)
96 return NULL;
97
98 table = kcalloc(nr_states, sizeof(*table), GFP_KERNEL);
99 if (!table)
100 goto free_pd;
101
102
103 for (i = 0, freq = 0; i < nr_states; i++, freq++) {
104
105
106
107
108
109 ret = cb->active_power(&power, &freq, cpu);
110 if (ret) {
111 pr_err("pd%d: invalid cap. state: %d\n", cpu, ret);
112 goto free_cs_table;
113 }
114
115
116
117
118
119 if (freq <= prev_freq) {
120 pr_err("pd%d: non-increasing freq: %lu\n", cpu, freq);
121 goto free_cs_table;
122 }
123
124
125
126
127
128 if (!power || power > EM_CPU_MAX_POWER) {
129 pr_err("pd%d: invalid power: %lu\n", cpu, power);
130 goto free_cs_table;
131 }
132
133 table[i].power = power;
134 table[i].frequency = prev_freq = freq;
135
136
137
138
139
140
141
142 opp_eff = freq / power;
143 if (opp_eff >= prev_opp_eff)
144 pr_warn("pd%d: hertz/watts ratio non-monotonically decreasing: em_cap_state %d >= em_cap_state%d\n",
145 cpu, i, i - 1);
146 prev_opp_eff = opp_eff;
147 }
148
149
150 fmax = (u64) table[nr_states - 1].frequency;
151 for (i = 0; i < nr_states; i++) {
152 table[i].cost = div64_u64(fmax * table[i].power,
153 table[i].frequency);
154 }
155
156 pd->table = table;
157 pd->nr_cap_states = nr_states;
158 cpumask_copy(to_cpumask(pd->cpus), span);
159
160 em_debug_create_pd(pd, cpu);
161
162 return pd;
163
164free_cs_table:
165 kfree(table);
166free_pd:
167 kfree(pd);
168
169 return NULL;
170}
171
172
173
174
175
176
177
178
179struct em_perf_domain *em_cpu_get(int cpu)
180{
181 return READ_ONCE(per_cpu(em_data, cpu));
182}
183EXPORT_SYMBOL_GPL(em_cpu_get);
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199int em_register_perf_domain(cpumask_t *span, unsigned int nr_states,
200 struct em_data_callback *cb)
201{
202 unsigned long cap, prev_cap = 0;
203 struct em_perf_domain *pd;
204 int cpu, ret = 0;
205
206 if (!span || !nr_states || !cb)
207 return -EINVAL;
208
209
210
211
212
213 mutex_lock(&em_pd_mutex);
214
215 for_each_cpu(cpu, span) {
216
217 if (READ_ONCE(per_cpu(em_data, cpu))) {
218 ret = -EEXIST;
219 goto unlock;
220 }
221
222
223
224
225
226 cap = arch_scale_cpu_capacity(NULL, cpu);
227 if (prev_cap && prev_cap != cap) {
228 pr_err("CPUs of %*pbl must have the same capacity\n",
229 cpumask_pr_args(span));
230 ret = -EINVAL;
231 goto unlock;
232 }
233 prev_cap = cap;
234 }
235
236
237 pd = em_create_pd(span, nr_states, cb);
238 if (!pd) {
239 ret = -EINVAL;
240 goto unlock;
241 }
242
243 for_each_cpu(cpu, span) {
244
245
246
247
248
249 smp_store_release(per_cpu_ptr(&em_data, cpu), pd);
250 }
251
252 pr_debug("Created perf domain %*pbl\n", cpumask_pr_args(span));
253unlock:
254 mutex_unlock(&em_pd_mutex);
255
256 return ret;
257}
258EXPORT_SYMBOL_GPL(em_register_perf_domain);
259