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 <asm/percpu.h>
23 #include <asm/smp.h>
24
25 #define CORE_PARKING_INCREMENT 1
26 #define CORE_PARKING_DECREMENT 2
27
28 static unsigned int core_parking_power(unsigned int event);
29 static unsigned int core_parking_performance(unsigned int event);
30
31 static uint32_t cur_idle_nums;
32 static unsigned int core_parking_cpunum[NR_CPUS] = {[0 ... NR_CPUS-1] = -1};
33
34 static struct core_parking_policy {
35 char name[30];
36 unsigned int (*next)(unsigned int event);
37 } *core_parking_policy;
38
39 static enum core_parking_controller {
40 POWER_FIRST,
41 PERFORMANCE_FIRST
42 } core_parking_controller = POWER_FIRST;
43
setup_core_parking_option(const char * str)44 static int __init 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 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 {
104 cpu = core_parking_cpunum[cur_idle_nums -1];
105 }
106 break;
107
108 default:
109 break;
110 }
111
112 return cpu;
113 }
114
core_parking_power(unsigned int event)115 static unsigned int 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 {
162 cpu = core_parking_cpunum[cur_idle_nums -1];
163 }
164 break;
165
166 default:
167 break;
168 }
169
170 return cpu;
171 }
172
core_parking_helper(void * data)173 long 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 )
180 return -EINVAL;
181
182 while ( cur_idle_nums < idle_nums )
183 {
184 cpu = core_parking_policy->next(CORE_PARKING_INCREMENT);
185 ret = cpu_down(cpu);
186 if ( ret )
187 return ret;
188 core_parking_cpunum[cur_idle_nums++] = cpu;
189 }
190
191 while ( cur_idle_nums > idle_nums )
192 {
193 cpu = core_parking_policy->next(CORE_PARKING_DECREMENT);
194 ret = cpu_up(cpu);
195 if ( ret )
196 return ret;
197 core_parking_cpunum[--cur_idle_nums] = -1;
198 }
199
200 return ret;
201 }
202
get_cur_idle_nums(void)203 uint32_t get_cur_idle_nums(void)
204 {
205 return cur_idle_nums;
206 }
207
208 static struct core_parking_policy power_first = {
209 .name = "power",
210 .next = core_parking_power,
211 };
212
213 static struct core_parking_policy performance_first = {
214 .name = "performance",
215 .next = core_parking_performance,
216 };
217
register_core_parking_policy(struct core_parking_policy * policy)218 static int register_core_parking_policy(struct core_parking_policy *policy)
219 {
220 if ( !policy || !policy->next )
221 return -EINVAL;
222
223 core_parking_policy = policy;
224 return 0;
225 }
226
core_parking_init(void)227 static int __init core_parking_init(void)
228 {
229 int ret = 0;
230
231 if ( core_parking_controller == PERFORMANCE_FIRST )
232 ret = register_core_parking_policy(&performance_first);
233 else
234 ret = register_core_parking_policy(&power_first);
235
236 return ret;
237 }
238 __initcall(core_parking_init);
239