ia64/xen-unstable

view xen/drivers/cpufreq/cpufreq.c @ 19149:efef232bbbdb

Consolidate cpufreq cmdline handling

... by moving as much of the option processing into cpufreq code as is
possible, by folding the cpufreq_governor option into the cpufreq one
(the governor name, if any, must be specified as the first thing after
the separator following "cpufreq=xen"), and by allowing each
governor to have an option processing routine.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Feb 03 18:12:51 2009 +0000 (2009-02-03)
parents 5544a96572bf
children ce391986ce35
line source
1 /*
2 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
3 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
4 * Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de>
5 * Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com>
6 *
7 * Feb 2008 - Liu Jinsong <jinsong.liu@intel.com>
8 * Add cpufreq limit change handle and per-cpu cpufreq add/del
9 * to cope with cpu hotplug
10 *
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 *
27 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28 */
30 #include <xen/types.h>
31 #include <xen/errno.h>
32 #include <xen/delay.h>
33 #include <xen/cpumask.h>
34 #include <xen/list.h>
35 #include <xen/sched.h>
36 #include <xen/string.h>
37 #include <xen/timer.h>
38 #include <xen/xmalloc.h>
39 #include <xen/guest_access.h>
40 #include <xen/domain.h>
41 #include <asm/bug.h>
42 #include <asm/io.h>
43 #include <asm/config.h>
44 #include <asm/processor.h>
45 #include <asm/percpu.h>
46 #include <acpi/acpi.h>
47 #include <acpi/cpufreq/cpufreq.h>
49 struct cpufreq_dom {
50 unsigned int dom;
51 cpumask_t map;
52 struct list_head node;
53 };
54 static LIST_HEAD(cpufreq_dom_list_head);
56 struct cpufreq_governor *cpufreq_opt_governor;
57 LIST_HEAD(cpufreq_governor_list);
59 struct cpufreq_governor *__find_governor(const char *governor)
60 {
61 struct cpufreq_governor *t;
63 if (!governor)
64 return NULL;
66 list_for_each_entry(t, &cpufreq_governor_list, governor_list)
67 if (!strnicmp(governor, t->name, CPUFREQ_NAME_LEN))
68 return t;
70 return NULL;
71 }
73 int cpufreq_register_governor(struct cpufreq_governor *governor)
74 {
75 if (!governor)
76 return -EINVAL;
78 if (__find_governor(governor->name) != NULL)
79 return -EEXIST;
81 list_add(&governor->governor_list, &cpufreq_governor_list);
82 return 0;
83 }
85 int cpufreq_unregister_governor(struct cpufreq_governor *governor)
86 {
87 int cpu = smp_processor_id();
88 struct cpufreq_policy *policy = cpufreq_cpu_policy[cpu];
90 if (!governor || !policy)
91 return -EINVAL;
93 /* error if unregister current cpufreq governor */
94 if (governor == policy->governor)
95 return -EBUSY;
97 if (__find_governor(governor->name) == NULL)
98 return -ENOENT;
100 list_del(&governor->governor_list);
101 return 0;
102 }
104 int cpufreq_limit_change(unsigned int cpu)
105 {
106 struct processor_performance *perf = &processor_pminfo[cpu]->perf;
107 struct cpufreq_policy *data = cpufreq_cpu_policy[cpu];
108 struct cpufreq_policy policy;
110 if (!cpu_online(cpu) || !data || !processor_pminfo[cpu])
111 return -ENODEV;
113 if ((perf->platform_limit < 0) ||
114 (perf->platform_limit >= perf->state_count))
115 return -EINVAL;
117 memcpy(&policy, data, sizeof(struct cpufreq_policy));
119 policy.max =
120 perf->states[perf->platform_limit].core_frequency * 1000;
122 return __cpufreq_set_policy(data, &policy);
123 }
125 int cpufreq_add_cpu(unsigned int cpu)
126 {
127 int ret = 0;
128 unsigned int firstcpu;
129 unsigned int dom, domexist = 0;
130 unsigned int j;
131 struct list_head *pos;
132 struct cpufreq_dom *cpufreq_dom = NULL;
133 struct cpufreq_policy new_policy;
134 struct cpufreq_policy *policy;
135 struct processor_performance *perf = &processor_pminfo[cpu]->perf;
137 /* to protect the case when Px was not controlled by xen */
138 if (!processor_pminfo[cpu] ||
139 !(perf->init & XEN_PX_INIT) ||
140 !cpu_online(cpu))
141 return -EINVAL;
143 if (cpufreq_cpu_policy[cpu])
144 return 0;
146 ret = cpufreq_statistic_init(cpu);
147 if (ret)
148 return ret;
150 dom = perf->domain_info.domain;
152 list_for_each(pos, &cpufreq_dom_list_head) {
153 cpufreq_dom = list_entry(pos, struct cpufreq_dom, node);
154 if (dom == cpufreq_dom->dom) {
155 domexist = 1;
156 break;
157 }
158 }
160 if (domexist) {
161 /* share policy with the first cpu since on same boat */
162 firstcpu = first_cpu(cpufreq_dom->map);
163 policy = cpufreq_cpu_policy[firstcpu];
165 cpufreq_cpu_policy[cpu] = policy;
166 cpu_set(cpu, cpufreq_dom->map);
167 cpu_set(cpu, policy->cpus);
169 /* domain coordination sanity check */
170 if ((perf->domain_info.coord_type !=
171 processor_pminfo[firstcpu]->perf.domain_info.coord_type) ||
172 (perf->domain_info.num_processors !=
173 processor_pminfo[firstcpu]->perf.domain_info.num_processors)) {
174 ret = -EINVAL;
175 goto err2;
176 }
178 printk(KERN_EMERG"adding CPU %u\n", cpu);
179 } else {
180 cpufreq_dom = xmalloc(struct cpufreq_dom);
181 if (!cpufreq_dom) {
182 cpufreq_statistic_exit(cpu);
183 return -ENOMEM;
184 }
185 memset(cpufreq_dom, 0, sizeof(struct cpufreq_dom));
186 cpufreq_dom->dom = dom;
187 cpu_set(cpu, cpufreq_dom->map);
188 list_add(&cpufreq_dom->node, &cpufreq_dom_list_head);
190 /* for the first cpu, setup policy and do init work */
191 policy = xmalloc(struct cpufreq_policy);
192 if (!policy) {
193 list_del(&cpufreq_dom->node);
194 xfree(cpufreq_dom);
195 cpufreq_statistic_exit(cpu);
196 return -ENOMEM;
197 }
198 memset(policy, 0, sizeof(struct cpufreq_policy));
199 policy->cpu = cpu;
200 cpu_set(cpu, policy->cpus);
201 cpufreq_cpu_policy[cpu] = policy;
203 ret = cpufreq_driver->init(policy);
204 if (ret)
205 goto err1;
206 printk(KERN_EMERG"CPU %u initialization completed\n", cpu);
207 }
209 /*
210 * After get full cpumap of the coordination domain,
211 * we can safely start gov here.
212 */
213 if (cpus_weight(cpufreq_dom->map) ==
214 perf->domain_info.num_processors) {
215 memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
216 policy->governor = NULL;
217 ret = __cpufreq_set_policy(policy, &new_policy);
218 if (ret) {
219 if (new_policy.governor == CPUFREQ_DEFAULT_GOVERNOR)
220 /* if default governor fail, cpufreq really meet troubles */
221 goto err2;
222 else {
223 /* grub option governor fail */
224 /* give one more chance to default gov */
225 memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
226 new_policy.governor = CPUFREQ_DEFAULT_GOVERNOR;
227 ret = __cpufreq_set_policy(policy, &new_policy);
228 if (ret)
229 goto err2;
230 }
231 }
232 }
234 return 0;
236 err2:
237 cpufreq_driver->exit(policy);
238 err1:
239 for_each_cpu_mask(j, cpufreq_dom->map) {
240 cpufreq_cpu_policy[j] = NULL;
241 cpufreq_statistic_exit(j);
242 }
244 list_del(&cpufreq_dom->node);
245 xfree(cpufreq_dom);
246 xfree(policy);
247 return ret;
248 }
250 int cpufreq_del_cpu(unsigned int cpu)
251 {
252 unsigned int dom, domexist = 0;
253 struct list_head *pos;
254 struct cpufreq_dom *cpufreq_dom = NULL;
255 struct cpufreq_policy *policy;
256 struct processor_performance *perf = &processor_pminfo[cpu]->perf;
258 /* to protect the case when Px was not controlled by xen */
259 if (!processor_pminfo[cpu] ||
260 !(perf->init & XEN_PX_INIT) ||
261 !cpu_online(cpu))
262 return -EINVAL;
264 if (!cpufreq_cpu_policy[cpu])
265 return 0;
267 dom = perf->domain_info.domain;
268 policy = cpufreq_cpu_policy[cpu];
270 list_for_each(pos, &cpufreq_dom_list_head) {
271 cpufreq_dom = list_entry(pos, struct cpufreq_dom, node);
272 if (dom == cpufreq_dom->dom) {
273 domexist = 1;
274 break;
275 }
276 }
278 if (!domexist)
279 return -EINVAL;
281 /* for the first cpu of the domain, stop gov */
282 if (cpus_weight(cpufreq_dom->map) ==
283 perf->domain_info.num_processors)
284 __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
286 cpufreq_cpu_policy[cpu] = NULL;
287 cpu_clear(cpu, policy->cpus);
288 cpu_clear(cpu, cpufreq_dom->map);
289 cpufreq_statistic_exit(cpu);
291 /* for the last cpu of the domain, clean room */
292 /* It's safe here to free freq_table, drv_data and policy */
293 if (!cpus_weight(cpufreq_dom->map)) {
294 cpufreq_driver->exit(policy);
295 list_del(&cpufreq_dom->node);
296 xfree(cpufreq_dom);
297 xfree(policy);
298 }
300 printk(KERN_EMERG"deleting CPU %u\n", cpu);
301 return 0;
302 }
304 static void print_PCT(struct xen_pct_register *ptr)
305 {
306 printk(KERN_INFO "\t_PCT: descriptor=%d, length=%d, space_id=%d, "
307 "bit_width=%d, bit_offset=%d, reserved=%d, address=%"PRId64"\n",
308 ptr->descriptor, ptr->length, ptr->space_id, ptr->bit_width,
309 ptr->bit_offset, ptr->reserved, ptr->address);
310 }
312 static void print_PSS(struct xen_processor_px *ptr, int count)
313 {
314 int i;
315 printk(KERN_INFO "\t_PSS: state_count=%d\n", count);
316 for (i=0; i<count; i++){
317 printk(KERN_INFO "\tState%d: %"PRId64"MHz %"PRId64"mW %"PRId64"us "
318 "%"PRId64"us 0x%"PRIx64" 0x%"PRIx64"\n",
319 i,
320 ptr[i].core_frequency,
321 ptr[i].power,
322 ptr[i].transition_latency,
323 ptr[i].bus_master_latency,
324 ptr[i].control,
325 ptr[i].status
326 );
327 }
328 }
330 static void print_PSD( struct xen_psd_package *ptr)
331 {
332 printk(KERN_INFO "\t_PSD: num_entries=%"PRId64" rev=%"PRId64
333 " domain=%"PRId64" coord_type=%"PRId64" num_processors=%"PRId64"\n",
334 ptr->num_entries, ptr->revision, ptr->domain, ptr->coord_type,
335 ptr->num_processors);
336 }
338 static void print_PPC(unsigned int platform_limit)
339 {
340 printk(KERN_INFO "\t_PPC: %d\n", platform_limit);
341 }
343 int set_px_pminfo(uint32_t acpi_id, struct xen_processor_performance *dom0_px_info)
344 {
345 int ret=0, cpuid;
346 struct processor_pminfo *pmpt;
347 struct processor_performance *pxpt;
349 cpuid = get_cpu_id(acpi_id);
350 if ( cpuid < 0 || !dom0_px_info)
351 {
352 ret = -EINVAL;
353 goto out;
354 }
355 printk(KERN_INFO "Set CPU acpi_id(%d) cpuid(%d) Px State info:\n",
356 acpi_id, cpuid);
358 pmpt = processor_pminfo[cpuid];
359 if ( !pmpt )
360 {
361 pmpt = xmalloc(struct processor_pminfo);
362 if ( !pmpt )
363 {
364 ret = -ENOMEM;
365 goto out;
366 }
367 memset(pmpt, 0, sizeof(*pmpt));
368 processor_pminfo[cpuid] = pmpt;
369 }
370 pxpt = &pmpt->perf;
371 pmpt->acpi_id = acpi_id;
372 pmpt->id = cpuid;
374 if ( dom0_px_info->flags & XEN_PX_PCT )
375 {
376 /* space_id check */
377 if (dom0_px_info->control_register.space_id !=
378 dom0_px_info->status_register.space_id)
379 {
380 ret = -EINVAL;
381 goto out;
382 }
384 #ifdef CONFIG_IA64
385 /* for IA64, currently it only supports FFH */
386 if (dom0_px_info->control_register.space_id !=
387 ACPI_ADR_SPACE_FIXED_HARDWARE)
388 {
389 ret = -EINVAL;
390 goto out;
391 }
392 #endif
394 memcpy ((void *)&pxpt->control_register,
395 (void *)&dom0_px_info->control_register,
396 sizeof(struct xen_pct_register));
397 memcpy ((void *)&pxpt->status_register,
398 (void *)&dom0_px_info->status_register,
399 sizeof(struct xen_pct_register));
400 print_PCT(&pxpt->control_register);
401 print_PCT(&pxpt->status_register);
402 }
404 if ( dom0_px_info->flags & XEN_PX_PSS )
405 {
406 /* capability check */
407 if (dom0_px_info->state_count <= 1)
408 {
409 ret = -EINVAL;
410 goto out;
411 }
413 if ( !(pxpt->states = xmalloc_array(struct xen_processor_px,
414 dom0_px_info->state_count)) )
415 {
416 ret = -ENOMEM;
417 goto out;
418 }
419 copy_from_guest(pxpt->states, dom0_px_info->states,
420 dom0_px_info->state_count);
421 pxpt->state_count = dom0_px_info->state_count;
422 print_PSS(pxpt->states,pxpt->state_count);
423 }
425 if ( dom0_px_info->flags & XEN_PX_PSD )
426 {
427 #ifdef CONFIG_X86
428 /* for X86, check domain coordination */
429 /* for IA64, _PSD is optional for current IA64 cpufreq algorithm */
430 if (dom0_px_info->shared_type != CPUFREQ_SHARED_TYPE_ALL &&
431 dom0_px_info->shared_type != CPUFREQ_SHARED_TYPE_ANY &&
432 dom0_px_info->shared_type != CPUFREQ_SHARED_TYPE_HW)
433 {
434 ret = -EINVAL;
435 goto out;
436 }
437 #endif
439 pxpt->shared_type = dom0_px_info->shared_type;
440 memcpy ((void *)&pxpt->domain_info,
441 (void *)&dom0_px_info->domain_info,
442 sizeof(struct xen_psd_package));
443 print_PSD(&pxpt->domain_info);
444 }
446 if ( dom0_px_info->flags & XEN_PX_PPC )
447 {
448 pxpt->platform_limit = dom0_px_info->platform_limit;
449 print_PPC(pxpt->platform_limit);
451 if ( pxpt->init == XEN_PX_INIT )
452 {
453 ret = cpufreq_limit_change(cpuid);
454 goto out;
455 }
456 }
458 if ( dom0_px_info->flags == ( XEN_PX_PCT | XEN_PX_PSS |
459 XEN_PX_PSD | XEN_PX_PPC ) )
460 {
461 pxpt->init = XEN_PX_INIT;
463 ret = cpufreq_cpu_init(cpuid);
464 goto out;
465 }
467 out:
468 return ret;
469 }
471 void __init cpufreq_cmdline_parse(char *str)
472 {
473 static struct cpufreq_governor *__initdata cpufreq_governors[] =
474 {
475 &cpufreq_gov_userspace,
476 &cpufreq_gov_dbs,
477 &cpufreq_gov_performance,
478 &cpufreq_gov_powersave
479 };
481 do {
482 char *val, *end = strchr(str, ',');
483 unsigned int i;
485 if ( end )
486 *end++ = '\0';
487 val = strchr(str, '=');
488 if ( val )
489 *val++ = '\0';
491 if ( !cpufreq_opt_governor )
492 {
493 if ( !val )
494 {
495 for ( i = 0; i < ARRAY_SIZE(cpufreq_governors); ++i )
496 if ( !strcmp(str, cpufreq_governors[i]->name) )
497 {
498 cpufreq_opt_governor = cpufreq_governors[i];
499 str = NULL;
500 break;
501 }
502 }
503 else
504 cpufreq_opt_governor = CPUFREQ_DEFAULT_GOVERNOR;
505 }
507 if ( str )
508 for ( i = 0; i < ARRAY_SIZE(cpufreq_governors); ++i )
509 if ( cpufreq_governors[i]->handle_option )
510 cpufreq_governors[i]->handle_option(str, val);
512 str = end;
513 } while ( str );
514 }