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