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/types.h>
19 #include <xen/cpu.h>
20 #include <xen/init.h>
21 #include <xen/cpumask.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 static const struct cp_policy {
34 char name[30];
35 unsigned int (*next)(unsigned int event);
36 } *__read_mostly core_parking_policy;
37
38 static enum core_parking_controller {
39 POWER_FIRST,
40 PERFORMANCE_FIRST
41 } core_parking_controller __initdata = POWER_FIRST;
42
setup_core_parking_option(const char * str)43 static int __init setup_core_parking_option(const char *str)
44 {
45 if ( !strcmp(str, "power") )
46 core_parking_controller = POWER_FIRST;
47 else if ( !strcmp(str, "performance") )
48 core_parking_controller = PERFORMANCE_FIRST;
49 else
50 return -EINVAL;
51
52 return 0;
53 }
54 custom_param("core_parking", setup_core_parking_option);
55
core_parking_performance(unsigned int event)56 static unsigned int core_parking_performance(unsigned int event)
57 {
58 unsigned int cpu = -1;
59
60 switch ( event )
61 {
62 case CORE_PARKING_INCREMENT:
63 {
64 int core_tmp, core_weight = -1;
65 int sibling_tmp, sibling_weight = -1;
66 cpumask_t core_candidate_map, sibling_candidate_map;
67 cpumask_clear(&core_candidate_map);
68 cpumask_clear(&sibling_candidate_map);
69
70 for_each_cpu(cpu, &cpu_online_map)
71 {
72 if ( cpu == 0 )
73 continue;
74
75 core_tmp = cpumask_weight(per_cpu(cpu_core_mask, cpu));
76 if ( core_weight < core_tmp )
77 {
78 core_weight = core_tmp;
79 cpumask_copy(&core_candidate_map, cpumask_of(cpu));
80 }
81 else if ( core_weight == core_tmp )
82 __cpumask_set_cpu(cpu, &core_candidate_map);
83 }
84
85 for_each_cpu(cpu, &core_candidate_map)
86 {
87 sibling_tmp = cpumask_weight(per_cpu(cpu_sibling_mask, cpu));
88 if ( sibling_weight < sibling_tmp )
89 {
90 sibling_weight = sibling_tmp;
91 cpumask_copy(&sibling_candidate_map, cpumask_of(cpu));
92 }
93 else if ( sibling_weight == sibling_tmp )
94 __cpumask_set_cpu(cpu, &sibling_candidate_map);
95 }
96
97 cpu = cpumask_first(&sibling_candidate_map);
98 }
99 break;
100
101 case CORE_PARKING_DECREMENT:
102 spin_lock(&accounting_lock);
103 cpu = core_parking_cpunum[cur_idle_nums - 1];
104 spin_unlock(&accounting_lock);
105 break;
106
107 default:
108 break;
109 }
110
111 return cpu;
112 }
113
core_parking_power(unsigned int event)114 static unsigned int core_parking_power(unsigned int event)
115 {
116 unsigned int cpu = -1;
117
118 switch ( event )
119 {
120 case CORE_PARKING_INCREMENT:
121 {
122 int core_tmp, core_weight = NR_CPUS + 1;
123 int sibling_tmp, sibling_weight = NR_CPUS + 1;
124 cpumask_t core_candidate_map, sibling_candidate_map;
125 cpumask_clear(&core_candidate_map);
126 cpumask_clear(&sibling_candidate_map);
127
128 for_each_cpu(cpu, &cpu_online_map)
129 {
130 if ( cpu == 0 )
131 continue;
132
133 core_tmp = cpumask_weight(per_cpu(cpu_core_mask, cpu));
134 if ( core_weight > core_tmp )
135 {
136 core_weight = core_tmp;
137 cpumask_copy(&core_candidate_map, cpumask_of(cpu));
138 }
139 else if ( core_weight == core_tmp )
140 __cpumask_set_cpu(cpu, &core_candidate_map);
141 }
142
143 for_each_cpu(cpu, &core_candidate_map)
144 {
145 sibling_tmp = cpumask_weight(per_cpu(cpu_sibling_mask, cpu));
146 if ( sibling_weight > sibling_tmp )
147 {
148 sibling_weight = sibling_tmp;
149 cpumask_copy(&sibling_candidate_map, cpumask_of(cpu));
150 }
151 else if ( sibling_weight == sibling_tmp )
152 __cpumask_set_cpu(cpu, &sibling_candidate_map);
153 }
154
155 cpu = cpumask_first(&sibling_candidate_map);
156 }
157 break;
158
159 case CORE_PARKING_DECREMENT:
160 spin_lock(&accounting_lock);
161 cpu = core_parking_cpunum[cur_idle_nums - 1];
162 spin_unlock(&accounting_lock);
163 break;
164
165 default:
166 break;
167 }
168
169 return cpu;
170 }
171
core_parking_helper(void * data)172 long core_parking_helper(void *data)
173 {
174 uint32_t idle_nums = (unsigned long)data;
175 unsigned int cpu;
176 int ret = 0;
177
178 if ( !core_parking_policy )
179 return -EINVAL;
180
181 while ( cur_idle_nums < idle_nums )
182 {
183 cpu = core_parking_policy->next(CORE_PARKING_INCREMENT);
184 ret = cpu_down(cpu);
185 if ( ret )
186 return ret;
187
188 spin_lock(&accounting_lock);
189 BUG_ON(cur_idle_nums >= ARRAY_SIZE(core_parking_cpunum));
190 core_parking_cpunum[cur_idle_nums++] = cpu;
191 spin_unlock(&accounting_lock);
192 }
193
194 while ( cur_idle_nums > idle_nums )
195 {
196 cpu = core_parking_policy->next(CORE_PARKING_DECREMENT);
197 ret = cpu_up(cpu);
198 if ( ret )
199 return ret;
200
201 if ( !core_parking_remove(cpu) )
202 {
203 ret = cpu_down(cpu);
204 if ( ret == -EEXIST )
205 ret = 0;
206 if ( ret )
207 break;
208 }
209 }
210
211 return ret;
212 }
213
core_parking_remove(unsigned int cpu)214 bool core_parking_remove(unsigned int cpu)
215 {
216 unsigned int i;
217 bool found = false;
218
219 spin_lock(&accounting_lock);
220
221 for ( i = 0; i < cur_idle_nums; ++i )
222 if ( core_parking_cpunum[i] == cpu )
223 {
224 found = true;
225 --cur_idle_nums;
226 break;
227 }
228
229 for ( ; i < cur_idle_nums; ++i )
230 core_parking_cpunum[i] = core_parking_cpunum[i + 1];
231
232 spin_unlock(&accounting_lock);
233
234 return found;
235 }
236
get_cur_idle_nums(void)237 uint32_t get_cur_idle_nums(void)
238 {
239 return cur_idle_nums;
240 }
241
242 static const struct cp_policy power_first = {
243 .name = "power",
244 .next = core_parking_power,
245 };
246
247 static const struct cp_policy performance_first = {
248 .name = "performance",
249 .next = core_parking_performance,
250 };
251
register_core_parking_policy(const struct cp_policy * policy)252 static int __init register_core_parking_policy(const struct cp_policy *policy)
253 {
254 if ( !policy || !policy->next )
255 return -EINVAL;
256
257 core_parking_policy = policy;
258 return 0;
259 }
260
core_parking_init(void)261 static int __init core_parking_init(void)
262 {
263 int ret = 0;
264
265 if ( core_parking_controller == PERFORMANCE_FIRST )
266 ret = register_core_parking_policy(&performance_first);
267 else
268 ret = register_core_parking_policy(&power_first);
269
270 return ret;
271 }
272 __initcall(core_parking_init);
273