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