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