1 /*
2 * core_parking.c - implement core parking according to dom0 requirement
3 *
4 * Copyright (C) 2012, Intel Corporation.
5 * Author: Liu, Jinsong <jinsong.liu@intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 */
17
18 #include <xen/alternative-call.h>
19 #include <xen/cpu.h>
20 #include <xen/cpumask.h>
21 #include <xen/init.h>
22 #include <xen/param.h>
23
24 #include <asm/smp.h>
25
26 #define CORE_PARKING_INCREMENT 1
27 #define CORE_PARKING_DECREMENT 2
28
29 static DEFINE_SPINLOCK(accounting_lock);
30 static uint32_t cur_idle_nums;
31 static unsigned int core_parking_cpunum[NR_CPUS] = {[0 ... NR_CPUS-1] = -1};
32
33 struct cp_policy {
34 char name[30];
35 unsigned int (*next)(unsigned int event);
36 };
37 static struct cp_policy __ro_after_init core_parking_policy;
38
39 static enum core_parking_controller {
40 POWER_FIRST,
41 PERFORMANCE_FIRST
42 } core_parking_controller __initdata = POWER_FIRST;
43
setup_core_parking_option(const char * str)44 static int __init cf_check setup_core_parking_option(const char *str)
45 {
46 if ( !strcmp(str, "power") )
47 core_parking_controller = POWER_FIRST;
48 else if ( !strcmp(str, "performance") )
49 core_parking_controller = PERFORMANCE_FIRST;
50 else
51 return -EINVAL;
52
53 return 0;
54 }
55 custom_param("core_parking", setup_core_parking_option);
56
core_parking_performance(unsigned int event)57 static unsigned int cf_check core_parking_performance(unsigned int event)
58 {
59 unsigned int cpu = -1;
60
61 switch ( event )
62 {
63 case CORE_PARKING_INCREMENT:
64 {
65 int core_tmp, core_weight = -1;
66 int sibling_tmp, sibling_weight = -1;
67 cpumask_t core_candidate_map, sibling_candidate_map;
68 cpumask_clear(&core_candidate_map);
69 cpumask_clear(&sibling_candidate_map);
70
71 for_each_cpu(cpu, &cpu_online_map)
72 {
73 if ( cpu == 0 )
74 continue;
75
76 core_tmp = cpumask_weight(per_cpu(cpu_core_mask, cpu));
77 if ( core_weight < core_tmp )
78 {
79 core_weight = core_tmp;
80 cpumask_copy(&core_candidate_map, cpumask_of(cpu));
81 }
82 else if ( core_weight == core_tmp )
83 __cpumask_set_cpu(cpu, &core_candidate_map);
84 }
85
86 for_each_cpu(cpu, &core_candidate_map)
87 {
88 sibling_tmp = cpumask_weight(per_cpu(cpu_sibling_mask, cpu));
89 if ( sibling_weight < sibling_tmp )
90 {
91 sibling_weight = sibling_tmp;
92 cpumask_copy(&sibling_candidate_map, cpumask_of(cpu));
93 }
94 else if ( sibling_weight == sibling_tmp )
95 __cpumask_set_cpu(cpu, &sibling_candidate_map);
96 }
97
98 cpu = cpumask_first(&sibling_candidate_map);
99 }
100 break;
101
102 case CORE_PARKING_DECREMENT:
103 spin_lock(&accounting_lock);
104 cpu = core_parking_cpunum[cur_idle_nums - 1];
105 spin_unlock(&accounting_lock);
106 break;
107
108 default:
109 break;
110 }
111
112 return cpu;
113 }
114
core_parking_power(unsigned int event)115 static unsigned int cf_check core_parking_power(unsigned int event)
116 {
117 unsigned int cpu = -1;
118
119 switch ( event )
120 {
121 case CORE_PARKING_INCREMENT:
122 {
123 int core_tmp, core_weight = NR_CPUS + 1;
124 int sibling_tmp, sibling_weight = NR_CPUS + 1;
125 cpumask_t core_candidate_map, sibling_candidate_map;
126 cpumask_clear(&core_candidate_map);
127 cpumask_clear(&sibling_candidate_map);
128
129 for_each_cpu(cpu, &cpu_online_map)
130 {
131 if ( cpu == 0 )
132 continue;
133
134 core_tmp = cpumask_weight(per_cpu(cpu_core_mask, cpu));
135 if ( core_weight > core_tmp )
136 {
137 core_weight = core_tmp;
138 cpumask_copy(&core_candidate_map, cpumask_of(cpu));
139 }
140 else if ( core_weight == core_tmp )
141 __cpumask_set_cpu(cpu, &core_candidate_map);
142 }
143
144 for_each_cpu(cpu, &core_candidate_map)
145 {
146 sibling_tmp = cpumask_weight(per_cpu(cpu_sibling_mask, cpu));
147 if ( sibling_weight > sibling_tmp )
148 {
149 sibling_weight = sibling_tmp;
150 cpumask_copy(&sibling_candidate_map, cpumask_of(cpu));
151 }
152 else if ( sibling_weight == sibling_tmp )
153 __cpumask_set_cpu(cpu, &sibling_candidate_map);
154 }
155
156 cpu = cpumask_first(&sibling_candidate_map);
157 }
158 break;
159
160 case CORE_PARKING_DECREMENT:
161 spin_lock(&accounting_lock);
162 cpu = core_parking_cpunum[cur_idle_nums - 1];
163 spin_unlock(&accounting_lock);
164 break;
165
166 default:
167 break;
168 }
169
170 return cpu;
171 }
172
core_parking_helper(void * data)173 long cf_check core_parking_helper(void *data)
174 {
175 uint32_t idle_nums = (unsigned long)data;
176 unsigned int cpu;
177 int ret = 0;
178
179 if ( !core_parking_policy.next )
180 return -EINVAL;
181
182 while ( cur_idle_nums < idle_nums )
183 {
184 cpu = alternative_call(core_parking_policy.next,
185 CORE_PARKING_INCREMENT);
186 ret = cpu_down(cpu);
187 if ( ret )
188 return ret;
189
190 spin_lock(&accounting_lock);
191 BUG_ON(cur_idle_nums >= ARRAY_SIZE(core_parking_cpunum));
192 core_parking_cpunum[cur_idle_nums++] = cpu;
193 spin_unlock(&accounting_lock);
194 }
195
196 while ( cur_idle_nums > idle_nums )
197 {
198 cpu = alternative_call(core_parking_policy.next,
199 CORE_PARKING_DECREMENT);
200 ret = cpu_up(cpu);
201 if ( ret )
202 return ret;
203
204 if ( !core_parking_remove(cpu) )
205 {
206 ret = cpu_down(cpu);
207 if ( ret == -EEXIST )
208 ret = 0;
209 if ( ret )
210 break;
211 }
212 }
213
214 return ret;
215 }
216
core_parking_remove(unsigned int cpu)217 bool core_parking_remove(unsigned int cpu)
218 {
219 unsigned int i;
220 bool found = false;
221
222 spin_lock(&accounting_lock);
223
224 for ( i = 0; i < cur_idle_nums; ++i )
225 if ( core_parking_cpunum[i] == cpu )
226 {
227 found = true;
228 --cur_idle_nums;
229 break;
230 }
231
232 for ( ; i < cur_idle_nums; ++i )
233 core_parking_cpunum[i] = core_parking_cpunum[i + 1];
234
235 spin_unlock(&accounting_lock);
236
237 return found;
238 }
239
get_cur_idle_nums(void)240 uint32_t get_cur_idle_nums(void)
241 {
242 return cur_idle_nums;
243 }
244
245 static const struct cp_policy __initconst_cf_clobber power_first = {
246 .name = "power",
247 .next = core_parking_power,
248 };
249
250 static const struct cp_policy __initconst_cf_clobber performance_first = {
251 .name = "performance",
252 .next = core_parking_performance,
253 };
254
register_core_parking_policy(const struct cp_policy * policy)255 static int __init register_core_parking_policy(const struct cp_policy *policy)
256 {
257 if ( !policy || !policy->next )
258 return -EINVAL;
259
260 core_parking_policy = *policy;
261 return 0;
262 }
263
core_parking_init(void)264 static int __init cf_check core_parking_init(void)
265 {
266 int ret = 0;
267
268 if ( core_parking_controller == PERFORMANCE_FIRST )
269 ret = register_core_parking_policy(&performance_first);
270 else
271 ret = register_core_parking_policy(&power_first);
272
273 return ret;
274 }
275 presmp_initcall(core_parking_init);
276