diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq.c')
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 85 |
1 files changed, 67 insertions, 18 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 109d62ccf651..6c6121b85a54 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
@@ -4,6 +4,9 @@ | |||
4 | * Copyright (C) 2001 Russell King | 4 | * Copyright (C) 2001 Russell King |
5 | * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> | 5 | * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> |
6 | * | 6 | * |
7 | * Oct 2005 - Ashok Raj <ashok.raj@intel.com> | ||
8 | * Added handling for CPU hotplug | ||
9 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
9 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
@@ -36,13 +39,6 @@ static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS]; | |||
36 | static DEFINE_SPINLOCK(cpufreq_driver_lock); | 39 | static DEFINE_SPINLOCK(cpufreq_driver_lock); |
37 | 40 | ||
38 | 41 | ||
39 | /* we keep a copy of all ->add'ed CPU's struct sys_device here; | ||
40 | * as it is only accessed in ->add and ->remove, no lock or reference | ||
41 | * count is necessary. | ||
42 | */ | ||
43 | static struct sys_device *cpu_sys_devices[NR_CPUS]; | ||
44 | |||
45 | |||
46 | /* internal prototypes */ | 42 | /* internal prototypes */ |
47 | static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); | 43 | static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); |
48 | static void handle_update(void *data); | 44 | static void handle_update(void *data); |
@@ -574,6 +570,9 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) | |||
574 | unsigned long flags; | 570 | unsigned long flags; |
575 | unsigned int j; | 571 | unsigned int j; |
576 | 572 | ||
573 | if (cpu_is_offline(cpu)) | ||
574 | return 0; | ||
575 | |||
577 | cpufreq_debug_disable_ratelimit(); | 576 | cpufreq_debug_disable_ratelimit(); |
578 | dprintk("adding CPU %u\n", cpu); | 577 | dprintk("adding CPU %u\n", cpu); |
579 | 578 | ||
@@ -582,7 +581,6 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) | |||
582 | * CPU because it is in the same boat. */ | 581 | * CPU because it is in the same boat. */ |
583 | policy = cpufreq_cpu_get(cpu); | 582 | policy = cpufreq_cpu_get(cpu); |
584 | if (unlikely(policy)) { | 583 | if (unlikely(policy)) { |
585 | cpu_sys_devices[cpu] = sys_dev; | ||
586 | dprintk("CPU already managed, adding link\n"); | 584 | dprintk("CPU already managed, adding link\n"); |
587 | sysfs_create_link(&sys_dev->kobj, &policy->kobj, "cpufreq"); | 585 | sysfs_create_link(&sys_dev->kobj, &policy->kobj, "cpufreq"); |
588 | cpufreq_debug_enable_ratelimit(); | 586 | cpufreq_debug_enable_ratelimit(); |
@@ -657,7 +655,6 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) | |||
657 | } | 655 | } |
658 | 656 | ||
659 | module_put(cpufreq_driver->owner); | 657 | module_put(cpufreq_driver->owner); |
660 | cpu_sys_devices[cpu] = sys_dev; | ||
661 | dprintk("initialization complete\n"); | 658 | dprintk("initialization complete\n"); |
662 | cpufreq_debug_enable_ratelimit(); | 659 | cpufreq_debug_enable_ratelimit(); |
663 | 660 | ||
@@ -682,7 +679,7 @@ err_out: | |||
682 | 679 | ||
683 | nomem_out: | 680 | nomem_out: |
684 | module_put(cpufreq_driver->owner); | 681 | module_put(cpufreq_driver->owner); |
685 | module_out: | 682 | module_out: |
686 | cpufreq_debug_enable_ratelimit(); | 683 | cpufreq_debug_enable_ratelimit(); |
687 | return ret; | 684 | return ret; |
688 | } | 685 | } |
@@ -698,6 +695,7 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev) | |||
698 | unsigned int cpu = sys_dev->id; | 695 | unsigned int cpu = sys_dev->id; |
699 | unsigned long flags; | 696 | unsigned long flags; |
700 | struct cpufreq_policy *data; | 697 | struct cpufreq_policy *data; |
698 | struct sys_device *cpu_sys_dev; | ||
701 | #ifdef CONFIG_SMP | 699 | #ifdef CONFIG_SMP |
702 | unsigned int j; | 700 | unsigned int j; |
703 | #endif | 701 | #endif |
@@ -710,7 +708,6 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev) | |||
710 | 708 | ||
711 | if (!data) { | 709 | if (!data) { |
712 | spin_unlock_irqrestore(&cpufreq_driver_lock, flags); | 710 | spin_unlock_irqrestore(&cpufreq_driver_lock, flags); |
713 | cpu_sys_devices[cpu] = NULL; | ||
714 | cpufreq_debug_enable_ratelimit(); | 711 | cpufreq_debug_enable_ratelimit(); |
715 | return -EINVAL; | 712 | return -EINVAL; |
716 | } | 713 | } |
@@ -725,14 +722,12 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev) | |||
725 | dprintk("removing link\n"); | 722 | dprintk("removing link\n"); |
726 | spin_unlock_irqrestore(&cpufreq_driver_lock, flags); | 723 | spin_unlock_irqrestore(&cpufreq_driver_lock, flags); |
727 | sysfs_remove_link(&sys_dev->kobj, "cpufreq"); | 724 | sysfs_remove_link(&sys_dev->kobj, "cpufreq"); |
728 | cpu_sys_devices[cpu] = NULL; | ||
729 | cpufreq_cpu_put(data); | 725 | cpufreq_cpu_put(data); |
730 | cpufreq_debug_enable_ratelimit(); | 726 | cpufreq_debug_enable_ratelimit(); |
731 | return 0; | 727 | return 0; |
732 | } | 728 | } |
733 | #endif | 729 | #endif |
734 | 730 | ||
735 | cpu_sys_devices[cpu] = NULL; | ||
736 | 731 | ||
737 | if (!kobject_get(&data->kobj)) { | 732 | if (!kobject_get(&data->kobj)) { |
738 | spin_unlock_irqrestore(&cpufreq_driver_lock, flags); | 733 | spin_unlock_irqrestore(&cpufreq_driver_lock, flags); |
@@ -761,7 +756,8 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev) | |||
761 | if (j == cpu) | 756 | if (j == cpu) |
762 | continue; | 757 | continue; |
763 | dprintk("removing link for cpu %u\n", j); | 758 | dprintk("removing link for cpu %u\n", j); |
764 | sysfs_remove_link(&cpu_sys_devices[j]->kobj, "cpufreq"); | 759 | cpu_sys_dev = get_cpu_sysdev(j); |
760 | sysfs_remove_link(&cpu_sys_dev->kobj, "cpufreq"); | ||
765 | cpufreq_cpu_put(data); | 761 | cpufreq_cpu_put(data); |
766 | } | 762 | } |
767 | } | 763 | } |
@@ -772,7 +768,6 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev) | |||
772 | down(&data->lock); | 768 | down(&data->lock); |
773 | if (cpufreq_driver->target) | 769 | if (cpufreq_driver->target) |
774 | __cpufreq_governor(data, CPUFREQ_GOV_STOP); | 770 | __cpufreq_governor(data, CPUFREQ_GOV_STOP); |
775 | cpufreq_driver->target = NULL; | ||
776 | up(&data->lock); | 771 | up(&data->lock); |
777 | 772 | ||
778 | kobject_unregister(&data->kobj); | 773 | kobject_unregister(&data->kobj); |
@@ -1119,17 +1114,30 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, | |||
1119 | unsigned int relation) | 1114 | unsigned int relation) |
1120 | { | 1115 | { |
1121 | int retval = -EINVAL; | 1116 | int retval = -EINVAL; |
1122 | lock_cpu_hotplug(); | 1117 | |
1118 | /* | ||
1119 | * Converted the lock_cpu_hotplug to preempt_disable() | ||
1120 | * and preempt_enable(). This is a bit kludgy and relies on how cpu | ||
1121 | * hotplug works. All we need is a guarantee that cpu hotplug won't make | ||
1122 | * progress on any cpu. Once we do preempt_disable(), this would ensure | ||
1123 | * that hotplug threads don't get onto this cpu, thereby delaying | ||
1124 | * the cpu remove process. | ||
1125 | * | ||
1126 | * We removed the lock_cpu_hotplug since we need to call this function | ||
1127 | * via cpu hotplug callbacks, which result in locking the cpu hotplug | ||
1128 | * thread itself. Agree this is not very clean, cpufreq community | ||
1129 | * could improve this if required. - Ashok Raj <ashok.raj@intel.com> | ||
1130 | */ | ||
1131 | preempt_disable(); | ||
1123 | dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu, | 1132 | dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu, |
1124 | target_freq, relation); | 1133 | target_freq, relation); |
1125 | if (cpu_online(policy->cpu) && cpufreq_driver->target) | 1134 | if (cpu_online(policy->cpu) && cpufreq_driver->target) |
1126 | retval = cpufreq_driver->target(policy, target_freq, relation); | 1135 | retval = cpufreq_driver->target(policy, target_freq, relation); |
1127 | unlock_cpu_hotplug(); | 1136 | preempt_enable(); |
1128 | return retval; | 1137 | return retval; |
1129 | } | 1138 | } |
1130 | EXPORT_SYMBOL_GPL(__cpufreq_driver_target); | 1139 | EXPORT_SYMBOL_GPL(__cpufreq_driver_target); |
1131 | 1140 | ||
1132 | |||
1133 | int cpufreq_driver_target(struct cpufreq_policy *policy, | 1141 | int cpufreq_driver_target(struct cpufreq_policy *policy, |
1134 | unsigned int target_freq, | 1142 | unsigned int target_freq, |
1135 | unsigned int relation) | 1143 | unsigned int relation) |
@@ -1416,6 +1424,45 @@ int cpufreq_update_policy(unsigned int cpu) | |||
1416 | } | 1424 | } |
1417 | EXPORT_SYMBOL(cpufreq_update_policy); | 1425 | EXPORT_SYMBOL(cpufreq_update_policy); |
1418 | 1426 | ||
1427 | static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb, | ||
1428 | unsigned long action, void *hcpu) | ||
1429 | { | ||
1430 | unsigned int cpu = (unsigned long)hcpu; | ||
1431 | struct cpufreq_policy *policy; | ||
1432 | struct sys_device *sys_dev; | ||
1433 | |||
1434 | sys_dev = get_cpu_sysdev(cpu); | ||
1435 | |||
1436 | if (sys_dev) { | ||
1437 | switch (action) { | ||
1438 | case CPU_ONLINE: | ||
1439 | cpufreq_add_dev(sys_dev); | ||
1440 | break; | ||
1441 | case CPU_DOWN_PREPARE: | ||
1442 | /* | ||
1443 | * We attempt to put this cpu in lowest frequency | ||
1444 | * possible before going down. This will permit | ||
1445 | * hardware-managed P-State to switch other related | ||
1446 | * threads to min or higher speeds if possible. | ||
1447 | */ | ||
1448 | policy = cpufreq_cpu_data[cpu]; | ||
1449 | if (policy) { | ||
1450 | cpufreq_driver_target(policy, policy->min, | ||
1451 | CPUFREQ_RELATION_H); | ||
1452 | } | ||
1453 | break; | ||
1454 | case CPU_DEAD: | ||
1455 | cpufreq_remove_dev(sys_dev); | ||
1456 | break; | ||
1457 | } | ||
1458 | } | ||
1459 | return NOTIFY_OK; | ||
1460 | } | ||
1461 | |||
1462 | static struct notifier_block cpufreq_cpu_notifier = | ||
1463 | { | ||
1464 | .notifier_call = cpufreq_cpu_callback, | ||
1465 | }; | ||
1419 | 1466 | ||
1420 | /********************************************************************* | 1467 | /********************************************************************* |
1421 | * REGISTER / UNREGISTER CPUFREQ DRIVER * | 1468 | * REGISTER / UNREGISTER CPUFREQ DRIVER * |
@@ -1476,6 +1523,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) | |||
1476 | } | 1523 | } |
1477 | 1524 | ||
1478 | if (!ret) { | 1525 | if (!ret) { |
1526 | register_cpu_notifier(&cpufreq_cpu_notifier); | ||
1479 | dprintk("driver %s up and running\n", driver_data->name); | 1527 | dprintk("driver %s up and running\n", driver_data->name); |
1480 | cpufreq_debug_enable_ratelimit(); | 1528 | cpufreq_debug_enable_ratelimit(); |
1481 | } | 1529 | } |
@@ -1507,6 +1555,7 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver) | |||
1507 | dprintk("unregistering driver %s\n", driver->name); | 1555 | dprintk("unregistering driver %s\n", driver->name); |
1508 | 1556 | ||
1509 | sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver); | 1557 | sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver); |
1558 | unregister_cpu_notifier(&cpufreq_cpu_notifier); | ||
1510 | 1559 | ||
1511 | spin_lock_irqsave(&cpufreq_driver_lock, flags); | 1560 | spin_lock_irqsave(&cpufreq_driver_lock, flags); |
1512 | cpufreq_driver = NULL; | 1561 | cpufreq_driver = NULL; |