From: Arun R Bharadwaj <hidden> Date: 2009-10-06 15:25:21
Hi
Please consider this for inclusion into the testing tree.
This patchset introduces cpuidle infrastructure to POWER, prototyping
for pSeries, and also does a major refactoring of current x86 idle
power management and a cleanup of cpuidle infrastructure.
Earlier discussions on the same can be found at:
v6 --> http://lkml.org/lkml/2009/9/22/180
v5 --> http://lkml.org/lkml/2009/9/22/26
v4 --> http://lkml.org/lkml/2009/9/1/133
v3 --> http://lkml.org/lkml/2009/8/27/124
v2 --> http://lkml.org/lkml/2009/8/26/233
v1 --> http://lkml.org/lkml/2009/8/19/150
Changes in this version:
-----------------------------------------
* Earlier implementation maintained a list of cpuidle devices and
the list was traversed to disable and enable the cpuidle devices
when switching governors. This list is not necessary and is removed.
Now, we get the per cpu structure cpuidle_devices and disable and
enable it for each online cpu while switching governors.
* Earlier iterations were buggy w.r.t the sysfs interface of the cpuidle.
This iteration removes the bug in PATCH 1/7.
* In PATCH 7/7, smt_snooze_delay was not being updated whenever it was
changed through the sysfs interface. Provide a helper routine to
update it.
* In PATCH 7/7, the idle loops are renamed to denote the idle states
to avoid any confusion.
thanks,
arun
From: Arun R Bharadwaj <hidden> Date: 2009-10-06 15:27:05
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
This patch cleans up drivers/cpuidle/cpuidle.c
Earlier cpuidle assumed pm_idle as the default idle loop. Break that
assumption and make it more generic. cpuidle_idle_call() which is the
main idle loop of cpuidle is to be called by architectures which have
registered to cpuidle.
Remove routines cpuidle_install/uninstall_idle_handler() and
cpuidle_kick_cpus() which are not needed anymore.
Signed-off-by: Arun R Bharadwaj <redacted>
---
drivers/cpuidle/cpuidle.c | 62 +++++----------------------------------------
drivers/cpuidle/cpuidle.h | 6 +---
drivers/cpuidle/driver.c | 4 --
drivers/cpuidle/governor.c | 13 +++------
drivers/cpuidle/sysfs.c | 34 +++++++++++++-----------
include/linux/cpuidle.h | 4 ++
6 files changed, 37 insertions(+), 86 deletions(-)
Index: linux.trees.git/drivers/cpuidle/cpuidle.c
===================================================================
@@ -47,7 +43,7 @@ static int __cpuidle_register_device(str**NOTE:nolocksorsemaphoresshouldbeusedhere*/-staticvoidcpuidle_idle_call(void)+voidcpuidle_idle_call(void){structcpuidle_device*dev=__get_cpu_var(cpuidle_devices);structcpuidle_state*target_state;
@@ -55,13 +51,10 @@ static void cpuidle_idle_call(void)/* check if the device is ready */if(!dev||!dev->enabled){-if(pm_idle_old)-pm_idle_old();-else#if defined(CONFIG_ARCH_HAS_DEFAULT_IDLE)-default_idle();+default_idle();#else-local_irq_enable();+local_irq_enable();#endifreturn;}
@@ -75,7 +68,11 @@ static void cpuidle_idle_call(void)hrtimer_peek_ahead_timers();#endif/* ask the governor for the next state */-next_state=cpuidle_curr_governor->select(dev);+if(dev->state_count>1)+next_state=cpuidle_curr_governor->select(dev);+else+next_state=0;+if(need_resched())return;target_state=&dev->states[next_state];
@@ -96,35 +93,11 @@ static void cpuidle_idle_call(void)}/**-*cpuidle_install_idle_handler-installsthecpuidleidleloophandler-*/-voidcpuidle_install_idle_handler(void)-{-if(enabled_devices&&(pm_idle!=cpuidle_idle_call)){-/* Make sure all changes finished before we switch to new idle */-smp_wmb();-pm_idle=cpuidle_idle_call;-}-}--/**-*cpuidle_uninstall_idle_handler-uninstallsthecpuidleidleloophandler-*/-voidcpuidle_uninstall_idle_handler(void)-{-if(enabled_devices&&pm_idle_old&&(pm_idle!=pm_idle_old)){-pm_idle=pm_idle_old;-cpuidle_kick_cpus();-}-}--/***cpuidle_pause_and_lock-temporarilydisablesCPUIDLE*/voidcpuidle_pause_and_lock(void){mutex_lock(&cpuidle_lock);-cpuidle_uninstall_idle_handler();}EXPORT_SYMBOL_GPL(cpuidle_pause_and_lock);
@@ -61,9 +59,8 @@ int cpuidle_switch_governor(struct cpuidif(gov){if(!try_module_get(cpuidle_curr_governor->owner))return-EINVAL;-list_for_each_entry(dev,&cpuidle_detected_devices,device_list)-cpuidle_enable_device(dev);-cpuidle_install_idle_handler();+for_each_online_cpu(cpu)+cpuidle_enable_device(per_cpu(cpuidle_devices,cpu));printk(KERN_INFO"cpuidle: using governor %s\n",gov->name);}
@@ -9,9 +9,7 @@/* For internal use only */externstructcpuidle_governor*cpuidle_curr_governor;-externstructcpuidle_driver*cpuidle_curr_driver;externstructlist_headcpuidle_governors;-externstructlist_headcpuidle_detected_devices;externstructmutexcpuidle_lock;externspinlock_tcpuidle_driver_lock;
@@ -27,7 +25,7 @@ extern int cpuidle_add_class_sysfs(strucexternvoidcpuidle_remove_class_sysfs(structsysdev_class*cls);externintcpuidle_add_state_sysfs(structcpuidle_device*device);externvoidcpuidle_remove_state_sysfs(structcpuidle_device*device);-externintcpuidle_add_sysfs(structsys_device*sysdev);-externvoidcpuidle_remove_sysfs(structsys_device*sysdev);+externintcpuidle_add_sysfs(structcpuidle_device*device);+externvoidcpuidle_remove_sysfs(structcpuidle_device*device);#endif /* __DRIVER_CPUIDLE_H */
@@ -311,6 +311,13 @@ int cpuidle_add_state_sysfs(struct cpuidinti,ret=-ENOMEM;structcpuidle_state_kobj*kobj;+init_completion(&device->kobj_unregister);++ret=cpuidle_add_sysfs(device);+if(ret){+module_put(cpuidle_curr_driver->owner);+returnret;+}/* state statistics */for(i=0;i<device->state_count;i++){kobj=kzalloc(sizeof(structcpuidle_state_kobj),GFP_KERNEL);
From: Arun R Bharadwaj <hidden> Date: 2009-10-06 15:31:24
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
Implement a list based registering mechanism for architectures which
have multiple sets of idle routines which are to be registered.
Currently, in x86 it is done by merely setting pm_idle = idle_routine
and managing this pm_idle pointer is messy.
To give an example of how this mechanism works:
In x86, initially, idle routine is selected from the set of poll/mwait/
c1e/default idle loops. So the selected idle loop is registered in cpuidle
as one idle state cpuidle devices. Once ACPI comes up, it registers
another set of idle states on top of this state. Again, suppose a module
registers another set of idle loops, it is added to this list.
This provides a clean way of registering and unregistering idle state
routines.
In the current implementation, pm_idle is set as the current idle routine
being used and the old idle routine has to be maintained and when a module
registers/unregisters an idle routine, confusion arises.
Signed-off-by: Arun R Bharadwaj <redacted>
---
drivers/cpuidle/cpuidle.c | 54 ++++++++++++++++++++++++++++++++++++++++------
include/linux/cpuidle.h | 1
2 files changed, 48 insertions(+), 7 deletions(-)
Index: linux.trees.git/drivers/cpuidle/cpuidle.c
===================================================================
From: Arun R Bharadwaj <hidden> Date: 2009-10-06 15:32:20
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
This patch cleans up x86 of all instances of pm_idle.
pm_idle which was earlier called from cpu_idle() idle loop
is replaced by cpuidle_idle_call.
x86 also registers to cpuidle when the idle routine is selected,
by populating the cpuidle_device data structure for each cpu.
This is replicated for apm module and for xen, which also used pm_idle.
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/x86/kernel/apm_32.c | 37 ++++++++++++++++++--
arch/x86/kernel/process.c | 79 ++++++++++++++++++++++++++++++++-----------
arch/x86/kernel/process_32.c | 3 +
arch/x86/kernel/process_64.c | 3 +
arch/x86/xen/setup.c | 22 +++++++++++
5 files changed, 118 insertions(+), 26 deletions(-)
Index: linux.trees.git/arch/x86/kernel/process.c
===================================================================
@@ -244,12 +245,6 @@ int sys_vfork(struct pt_regs *regs)unsignedlongboot_option_idle_override=0;EXPORT_SYMBOL(boot_option_idle_override);-/*-*Powermanagementidlefunction,ifany..-*/-void(*pm_idle)(void);-EXPORT_SYMBOL(pm_idle);-#ifdef CONFIG_X86_32/**ThishaltmagicwasaworkaroundforancientfloppyDMA
@@ -329,17 +324,15 @@ static void do_nothing(void *unused)}/*-*cpu_idle_wait-UsedtoensurethatalltheCPUsdiscardoldvalueof-*pm_idleandupdatetonewpm_idlevalue.Requiredwhilechangingpm_idle-*handleronSMPsystems.+*cpu_idle_wait-RequiredwhilechangingidleroutinehandleronSMPsystems.*-*Callermusthavechangedpm_idletothenewvaluebeforethecall.Old-*pm_idlevaluewillnotbeusedbyanyCPUafterthereturnofthisfunction.+*Callermusthavechangedidleroutinetothenewvaluebeforethecall.Old+*valuewillnotbeusedbyanyCPUafterthereturnofthisfunction.*/voidcpu_idle_wait(void){smp_mb();-/* kick all the CPUs so that they exit out of pm_idle */+/* kick all the CPUs so that they exit out of idle loop */smp_call_function(do_nothing,NULL,1);}EXPORT_SYMBOL_GPL(cpu_idle_wait);
@@ -518,15 +511,59 @@ static void c1e_idle(void)default_idle();}+staticvoid(*local_idle)(void);+DEFINE_PER_CPU(structcpuidle_device,idle_devices);++structcpuidle_drivercpuidle_default_driver={+.name="cpuidle_default",+};++staticintlocal_idle_loop(structcpuidle_device*dev,structcpuidle_state*st)+{+ktime_tt1,t2;+s64diff;+intret;++t1=ktime_get();+local_idle();+t2=ktime_get();++diff=ktime_to_us(ktime_sub(t2,t1));+if(diff>INT_MAX)+diff=INT_MAX;+ret=(int)diff;++returnret;+}++staticintsetup_cpuidle_simple(void)+{+structcpuidle_device*dev;+intcpu;++if(!cpuidle_curr_driver)+cpuidle_register_driver(&cpuidle_default_driver);++for_each_online_cpu(cpu){+dev=&per_cpu(idle_devices,cpu);+dev->cpu=cpu;+dev->states[0].enter=local_idle_loop;+dev->state_count=1;+cpuidle_register_device(dev);+}+return0;+}+device_initcall(setup_cpuidle_simple);+void__cpuinitselect_idle_routine(conststructcpuinfo_x86*c){#ifdef CONFIG_SMP-if(pm_idle==poll_idle&&smp_num_siblings>1){+if(local_idle==poll_idle&&smp_num_siblings>1){printk(KERN_WARNING"WARNING: polling idle and HT enabled,"" performance may degrade.\n");}#endif-if(pm_idle)+if(local_idle)return;if(cpu_has(c,X86_FEATURE_MWAIT)&&mwait_usable(c)){
@@ -534,18 +571,20 @@ void __cpuinit select_idle_routine(const*OneCPUsupportsmwait=>AllCPUssupportsmwait*/printk(KERN_INFO"using mwait in idle threads.\n");-pm_idle=mwait_idle;+local_idle=mwait_idle;}elseif(check_c1e_idle(c)){printk(KERN_INFO"using C1E aware idle routine\n");-pm_idle=c1e_idle;+local_idle=c1e_idle;}else-pm_idle=default_idle;+local_idle=default_idle;++return;}void__initinit_c1e_mask(void){/* If we're using c1e_idle, we need to allocate c1e_mask. */-if(pm_idle==c1e_idle)+if(local_idle==c1e_idle)zalloc_cpumask_var(&c1e_mask,GFP_KERNEL);}
@@ -142,7 +143,7 @@ void cpu_idle(void)enter_idle();/* Don't trace irqs off for idle */stop_critical_timings();-pm_idle();+cpuidle_idle_call();start_critical_timings();/* In many cases the interrupt that ended idlehasalreadycalledexit_idle.Butsomeidle
From: Arun R Bharadwaj <hidden> Date: 2009-10-06 15:33:10
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
This patch enables the cpuidle option in Kconfig for pSeries.
Currently cpuidle infrastructure is enabled only for x86 and ARM.
This code is almost completely borrowed from x86 to enable
cpuidle for pSeries.
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/powerpc/Kconfig | 17 +++++++++++++++++
arch/powerpc/include/asm/system.h | 2 ++
arch/powerpc/kernel/idle.c | 19 +++++++++++++++++++
3 files changed, 38 insertions(+)
Index: linux.trees.git/arch/powerpc/Kconfig
===================================================================
@@ -243,6 +246,20 @@ source "kernel/Kconfig.freezer"source"arch/powerpc/sysdev/Kconfig"source"arch/powerpc/platforms/Kconfig"+menu"Power management options"++source"drivers/cpuidle/Kconfig"++configPSERIES_PROCESSOR_IDLE+bool"Idle Power Management Support for pSeries"+depends onPPC_PSERIES&&CPU_IDLE+defaulty+help+IdlePowerManagementSupportforpSeries.Thishooksontocpuidle+infrastructuretohelpinidlecpupowermanagement.++endmenu+menu"Kernel options"configHIGHMEM
@@ -102,6 +102,25 @@ void cpu_idle(void)}}+staticvoiddo_nothing(void*unused)+{+}++/*+*cpu_idle_wait-UsedtoensurethatalltheCPUscomeoutoftheold+*idleloopandstartusingthenewidleloop.+*RequiredwhilechangingidlehandleronSMPsystems.+*Callermusthavechangedidlehandlertothenewvaluebeforethecall.+*/+voidcpu_idle_wait(void)+{+/* Ensure that new value of ppc_md.power_save is set */+smp_mb();+/* kick all the CPUs so that they exit out of ppc_md.power_save */+smp_call_function(do_nothing,NULL,1);+}+EXPORT_SYMBOL_GPL(cpu_idle_wait);+intpowersave_nap;#ifdef CONFIG_SYSCTL
From: Arun R Bharadwaj <hidden> Date: 2009-10-06 15:34:51
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
This patch removes the routines, pseries_shared_idle_sleep and
pseries_dedicated_idle_sleep, since this is implemented as a part
of arch/powerpc/platform/pseries/processor_idle.c
Also, similar to x86, call cpuidle_idle_call from cpu_idle() idle
loop instead of ppc_md.power_save.
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/powerpc/kernel/idle.c | 50 +++++++-----------
arch/powerpc/platforms/pseries/setup.c | 89 ---------------------------------
2 files changed, 22 insertions(+), 117 deletions(-)
Index: linux.trees.git/arch/powerpc/platforms/pseries/setup.c
===================================================================
@@ -75,9 +75,6 @@ EXPORT_SYMBOL(CMO_PageSize);intfwnmi_active;/* TRUE if an FWNMI handler is present */-staticvoidpseries_shared_idle_sleep(void);-staticvoidpseries_dedicated_idle_sleep(void);-staticstructdevice_node*pSeries_mpic_node;staticvoidpSeries_show_cpuinfo(structseq_file*m)
@@ -60,35 +61,26 @@ void cpu_idle(void)while(!need_resched()&&!cpu_should_die()){ppc64_runlatch_off();-if(ppc_md.power_save){-clear_thread_flag(TIF_POLLING_NRFLAG);-/*-*smp_mbissoclearingofTIF_POLLING_NRFLAG-*isorderedw.r.t.need_resched()test.-*/-smp_mb();-local_irq_disable();--/* Don't trace irqs off for idle */-stop_critical_timings();--/* check again after disabling irqs */-if(!need_resched()&&!cpu_should_die())-ppc_md.power_save();--start_critical_timings();--local_irq_enable();-set_thread_flag(TIF_POLLING_NRFLAG);--}else{-/*-*Gointolowthreadpriorityandpossibly-*lowpowermode.-*/-HMT_low();-HMT_very_low();-}+clear_thread_flag(TIF_POLLING_NRFLAG);+/*+*smp_mbissoclearingofTIF_POLLING_NRFLAG+*isorderedw.r.t.need_resched()test.+*/+smp_mb();+local_irq_disable();++/* Don't trace irqs off for idle */+stop_critical_timings();++/* check again after disabling irqs */+if(!need_resched()&&!cpu_should_die())+cpuidle_idle_call();++start_critical_timings();++local_irq_enable();+set_thread_flag(TIF_POLLING_NRFLAG);+}HMT_medium();
From: Arun R Bharadwaj <hidden> Date: 2009-10-06 15:35:35
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
In arch/powerpc/kernel/idle.c create a default_idle() routine by moving
the failover condition of the cpu_idle() idle loop. This is needed by
cpuidle infrastructure to call default_idle when other idle routines
are not yet registered. Functionality remains the same, but the code is
slightly moved around.
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/powerpc/Kconfig | 3 +++
arch/powerpc/include/asm/system.h | 1 +
arch/powerpc/kernel/idle.c | 6 ++++++
3 files changed, 10 insertions(+)
Index: linux.trees.git/arch/powerpc/Kconfig
===================================================================
@@ -218,6 +218,7 @@ extern unsigned long klimit;externvoid*alloc_maybe_bootmem(size_tsize,gfp_tmask);externvoid*zalloc_maybe_bootmem(size_tsize,gfp_tmask);+externvoiddefault_idle(void);externintpowersave_nap;/* set if nap mode can be used in idle loop *//*
From: Arun R Bharadwaj <hidden> Date: 2009-10-06 15:36:30
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
This patch creates arch/powerpc/platforms/pseries/processor_idle.c,
which implements the cpuidle infrastructure for pseries.
It implements a pseries_cpuidle_loop() which would be the main idle loop
called from cpu_idle(). It makes decision of entering either
dedicated_snooze_loop or dedicated_cede_loop for dedicated lpar and
shared_cede_loop for shared lpar processor based on the
decision taken by the cpuidle governor.
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/powerpc/include/asm/system.h | 1
arch/powerpc/kernel/sysfs.c | 2
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/processor_idle.c | 208 ++++++++++++++++++++++++
arch/powerpc/platforms/pseries/pseries.h | 8
5 files changed, 220 insertions(+)
Index: linux.trees.git/arch/powerpc/platforms/pseries/Makefile
===================================================================
From: Arun R Bharadwaj <hidden> Date: 2009-10-06 16:36:23
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
Hi
Please consider this for inclusion into the testing tree.
This patchset introduces cpuidle infrastructure to POWER, prototyping
for pSeries, and also does a major refactoring of current x86 idle
power management and a cleanup of cpuidle infrastructure.
Earlier discussions on the same can be found at:
v6 --> http://lkml.org/lkml/2009/9/22/180
v5 --> http://lkml.org/lkml/2009/9/22/26
v4 --> http://lkml.org/lkml/2009/9/1/133
v3 --> http://lkml.org/lkml/2009/8/27/124
v2 --> http://lkml.org/lkml/2009/8/26/233
v1 --> http://lkml.org/lkml/2009/8/19/150
Changes in this version:
-----------------------------------------
* Earlier implementation maintained a list of cpuidle devices and
the list was traversed to disable and enable the cpuidle devices
when switching governors. This list is not necessary and is removed.
Now, we get the per cpu structure cpuidle_devices and disable and
enable it for each online cpu while switching governors.
* Earlier iterations were buggy w.r.t the sysfs interface of the cpuidle.
This iteration removes the bug in PATCH 1/7.
* In PATCH 7/7, smt_snooze_delay was not being updated whenever it was
changed through the sysfs interface. Provide a helper routine to
update it.
* In PATCH 7/7, the idle loops are renamed to denote the idle states
to avoid any confusion.
Also, the per-cpu nature of registration/unregistration of cpuidle
has been maintained as ACPI needs this.
* Peter Zijlstra [off-list ref] [2009-10-06 20:04:39]:
On Tue, 2009-10-06 at 22:05 +0530, Arun R Bharadwaj wrote:
quoted
Also, the per-cpu nature of registration/unregistration of cpuidle
has been maintained as ACPI needs this.
Right, so can't we ditch that and have acpi default to the lowest common
C-state and warn when various cpus report different C-states?
Hi Peter,
As Arjan mentioned previously, the per-cpu registration has to stay
for x86 for now due to legacy ACPI compatibility. Breaking that may
break lot of existing users and we do not have a clean fallback
method.
As far as powerpc is concerned, we can work with a single global
registration. However we would like to have the same interface across
different archs.
With the new re-factoring (v7), Arun has killed most of the list
traversal and linking between various cpu's cpuidle_driver structures.
Now we have a per-cpu stack of registered devices and we lookup the
structs using online cpumasks. The cpuidle_driver structure has list
of idle routing pointers (struct cpuidle_state) and rest of it is
statistics that needs to be maintained at a per-cpu level anyway. All
that is duplicated here is the array of idle routines (struct
cpuidle_state) on each cpu.
The objective of the refactoring is to have a single common idle
routine management framework (remove pm_idle) and we have it done
through cpuidle registration framework. We can incrementally remove
the per-cpu registration later easily by splitting the cpuidle_driver
structure.
--Vaidy
* Peter Zijlstra [off-list ref] [2009-10-06 20:04:39]:
quoted
On Tue, 2009-10-06 at 22:05 +0530, Arun R Bharadwaj wrote:
quoted
Also, the per-cpu nature of registration/unregistration of cpuidle
has been maintained as ACPI needs this.
Right, so can't we ditch that and have acpi default to the lowest common
C-state and warn when various cpus report different C-states?
Hi Peter,
As Arjan mentioned previously, the per-cpu registration has to stay
for x86 for now due to legacy ACPI compatibility. Breaking that may
break lot of existing users and we do not have a clean fallback
method.
As far as powerpc is concerned, we can work with a single global
registration. However we would like to have the same interface across
different archs.
With the new re-factoring (v7), Arun has killed most of the list
traversal and linking between various cpu's cpuidle_driver structures.
Now we have a per-cpu stack of registered devices and we lookup the
structs using online cpumasks. The cpuidle_driver structure has list
of idle routing pointers (struct cpuidle_state) and rest of it is
statistics that needs to be maintained at a per-cpu level anyway. All
that is duplicated here is the array of idle routines (struct
cpuidle_state) on each cpu.
The objective of the refactoring is to have a single common idle
routine management framework (remove pm_idle) and we have it done
through cpuidle registration framework. We can incrementally remove
the per-cpu registration later easily by splitting the cpuidle_driver
structure.
Yes, incremental refactoring makes the most sense from the do not
break as you refactor point of view.
--
Balbir
From: Peter Zijlstra <hidden> Date: 2009-10-07 13:02:26
On Wed, 2009-10-07 at 16:56 +0530, Vaidyanathan Srinivasan wrote:
* Peter Zijlstra [off-list ref] [2009-10-06 20:04:39]:
quoted
On Tue, 2009-10-06 at 22:05 +0530, Arun R Bharadwaj wrote:
quoted
Also, the per-cpu nature of registration/unregistration of cpuidle
has been maintained as ACPI needs this.
Right, so can't we ditch that and have acpi default to the lowest
common
quoted
C-state and warn when various cpus report different C-states?
Hi Peter,
As Arjan mentioned previously, the per-cpu registration has to stay
for x86 for now due to legacy ACPI compatibility. Breaking that may
break lot of existing users and we do not have a clean fallback
method.
From what I understood some broken ass bioses report different C state
availability on different CPUs in the same SMP system.
I'm suggesting to work around that by limiting all CPUs to the subset of
C states reported on all CPUs, instead of the current mess.
I haven't heard anybody tell me why that wouldn't be possible on x86
From: Peter Zijlstra <hidden> Date: 2009-10-07 13:21:44
On Wed, 2009-10-07 at 17:17 +0530, Balbir Singh wrote:
quoted
The objective of the refactoring is to have a single common idle
routine management framework (remove pm_idle) and we have it done
through cpuidle registration framework. We can incrementally remove
the per-cpu registration later easily by splitting the cpuidle_driver
structure.
Yes, incremental refactoring makes the most sense from the do not
break as you refactor point of view.
Sure,.. but I would have though getting rid of the per-cpu-ish-ness
would have made the latter patches in this series easier. But maybe I'm
lazy ;-)
Let me go over the patches one more time, but they do look ok.
From: Arun R Bharadwaj <hidden> Date: 2009-10-07 13:52:11
* Arun R Bharadwaj [off-list ref] [2009-10-06 21:05:22]:
* Arun R Bharadwaj [off-list ref] [2009-10-06 20:54:21]:
Please consider this updated PATCH 7/7 instead of the earlier one.
The earlier one had a late_initcall(pseries_processor_idle_init),
which caused a panic when cpuidle_enable_device() was called from
cpuidle_switch_governor(). This is because registration of cpuidle
devices was happening at a later stage. So change this to a
device_initcall() to get rid of the panic.
---
This patch creates arch/powerpc/platforms/pseries/processor_idle.c,
which implements the cpuidle infrastructure for pseries.
It implements a pseries_cpuidle_loop() which would be the main idle loop
called from cpu_idle(). It makes decision of entering either
dedicated_snooze_loop or dedicated_cede_loop for dedicated lpar and
shared_cede_loop for shared lpar processor based on the
decision taken by the cpuidle governor.
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/powerpc/include/asm/system.h | 1
arch/powerpc/kernel/sysfs.c | 2
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/processor_idle.c | 208 ++++++++++++++++++++++++
arch/powerpc/platforms/pseries/pseries.h | 8
5 files changed, 220 insertions(+)
Index: linux.trees.git/arch/powerpc/platforms/pseries/Makefile
===================================================================
@@ -244,12 +245,6 @@ int sys_vfork(struct pt_regs *regs)unsignedlongboot_option_idle_override=0;EXPORT_SYMBOL(boot_option_idle_override);-/*-*Powermanagementidlefunction,ifany..-*/-void(*pm_idle)(void);-EXPORT_SYMBOL(pm_idle);-#ifdef CONFIG_X86_32/**ThishaltmagicwasaworkaroundforancientfloppyDMA
@@ -329,17 +324,15 @@ static void do_nothing(void *unused)}/*-*cpu_idle_wait-UsedtoensurethatalltheCPUsdiscardoldvalueof-*pm_idleandupdatetonewpm_idlevalue.Requiredwhilechangingpm_idle-*handleronSMPsystems.+*cpu_idle_wait-RequiredwhilechangingidleroutinehandleronSMPsystems.*-*Callermusthavechangedpm_idletothenewvaluebeforethecall.Old-*pm_idlevaluewillnotbeusedbyanyCPUafterthereturnofthisfunction.+*Callermusthavechangedidleroutinetothenewvaluebeforethecall.Old+*valuewillnotbeusedbyanyCPUafterthereturnofthisfunction.*/voidcpu_idle_wait(void){smp_mb();-/* kick all the CPUs so that they exit out of pm_idle */+/* kick all the CPUs so that they exit out of idle loop */smp_call_function(do_nothing,NULL,1);}EXPORT_SYMBOL_GPL(cpu_idle_wait);
@@ -518,15 +511,59 @@ static void c1e_idle(void)default_idle();}+staticvoid(*local_idle)(void);+DEFINE_PER_CPU(structcpuidle_device,idle_devices);++structcpuidle_drivercpuidle_default_driver={+.name="cpuidle_default",+};++staticintlocal_idle_loop(structcpuidle_device*dev,structcpuidle_state*st)+{+ktime_tt1,t2;+s64diff;+intret;++t1=ktime_get();+local_idle();+t2=ktime_get();++diff=ktime_to_us(ktime_sub(t2,t1));+if(diff>INT_MAX)+diff=INT_MAX;+ret=(int)diff;++returnret;+}++staticintsetup_cpuidle_simple(void)+{+structcpuidle_device*dev;+intcpu;++if(!cpuidle_curr_driver)+cpuidle_register_driver(&cpuidle_default_driver);++for_each_online_cpu(cpu){+dev=&per_cpu(idle_devices,cpu);+dev->cpu=cpu;+dev->states[0].enter=local_idle_loop;+dev->state_count=1;+cpuidle_register_device(dev);+}+return0;+}+device_initcall(setup_cpuidle_simple);+void__cpuinitselect_idle_routine(conststructcpuinfo_x86*c){#ifdef CONFIG_SMP-if(pm_idle==poll_idle&&smp_num_siblings>1){+if(local_idle==poll_idle&&smp_num_siblings>1){printk(KERN_WARNING"WARNING: polling idle and HT enabled,"" performance may degrade.\n");}#endif-if(pm_idle)+if(local_idle)return;if(cpu_has(c,X86_FEATURE_MWAIT)&&mwait_usable(c)){
@@ -534,18 +571,20 @@ void __cpuinit select_idle_routine(const*OneCPUsupportsmwait=>AllCPUssupportsmwait*/printk(KERN_INFO"using mwait in idle threads.\n");-pm_idle=mwait_idle;+local_idle=mwait_idle;}elseif(check_c1e_idle(c)){printk(KERN_INFO"using C1E aware idle routine\n");-pm_idle=c1e_idle;+local_idle=c1e_idle;}else-pm_idle=default_idle;+local_idle=default_idle;++return;}void__initinit_c1e_mask(void){/* If we're using c1e_idle, we need to allocate c1e_mask. */-if(pm_idle==c1e_idle)+if(local_idle==c1e_idle)zalloc_cpumask_var(&c1e_mask,GFP_KERNEL);}
@@ -567,7 +606,7 @@ static int __init idle_setup(char *str)*TocontinuetoloadtheCPUidledriver,don'ttouch*theboot_option_idle_override.*/-pm_idle=default_idle;+local_idle=default_idle;idle_halt=1;return0;}elseif(!strcmp(str,"nomwait")){
What guarantees that the cpuidle bits actually select this
cpuidle_default driver when you do idle=poll?
Also, cpuidle already has a poll loop in it, why duplicate that?
@@ -244,12 +245,6 @@ int sys_vfork(struct pt_regs *regs)unsignedlongboot_option_idle_override=0;EXPORT_SYMBOL(boot_option_idle_override);-/*-*Powermanagementidlefunction,ifany..-*/-void(*pm_idle)(void);-EXPORT_SYMBOL(pm_idle);-#ifdef CONFIG_X86_32/**ThishaltmagicwasaworkaroundforancientfloppyDMA
@@ -329,17 +324,15 @@ static void do_nothing(void *unused)}/*-*cpu_idle_wait-UsedtoensurethatalltheCPUsdiscardoldvalueof-*pm_idleandupdatetonewpm_idlevalue.Requiredwhilechangingpm_idle-*handleronSMPsystems.+*cpu_idle_wait-RequiredwhilechangingidleroutinehandleronSMPsystems.*-*Callermusthavechangedpm_idletothenewvaluebeforethecall.Old-*pm_idlevaluewillnotbeusedbyanyCPUafterthereturnofthisfunction.+*Callermusthavechangedidleroutinetothenewvaluebeforethecall.Old+*valuewillnotbeusedbyanyCPUafterthereturnofthisfunction.*/voidcpu_idle_wait(void){smp_mb();-/* kick all the CPUs so that they exit out of pm_idle */+/* kick all the CPUs so that they exit out of idle loop */smp_call_function(do_nothing,NULL,1);}EXPORT_SYMBOL_GPL(cpu_idle_wait);
@@ -518,15 +511,59 @@ static void c1e_idle(void)default_idle();}+staticvoid(*local_idle)(void);+DEFINE_PER_CPU(structcpuidle_device,idle_devices);++structcpuidle_drivercpuidle_default_driver={+.name="cpuidle_default",+};++staticintlocal_idle_loop(structcpuidle_device*dev,structcpuidle_state*st)+{+ktime_tt1,t2;+s64diff;+intret;++t1=ktime_get();+local_idle();+t2=ktime_get();++diff=ktime_to_us(ktime_sub(t2,t1));+if(diff>INT_MAX)+diff=INT_MAX;+ret=(int)diff;++returnret;+}++staticintsetup_cpuidle_simple(void)+{+structcpuidle_device*dev;+intcpu;++if(!cpuidle_curr_driver)+cpuidle_register_driver(&cpuidle_default_driver);++for_each_online_cpu(cpu){+dev=&per_cpu(idle_devices,cpu);+dev->cpu=cpu;+dev->states[0].enter=local_idle_loop;+dev->state_count=1;+cpuidle_register_device(dev);+}+return0;+}+device_initcall(setup_cpuidle_simple);+void__cpuinitselect_idle_routine(conststructcpuinfo_x86*c){#ifdef CONFIG_SMP-if(pm_idle==poll_idle&&smp_num_siblings>1){+if(local_idle==poll_idle&&smp_num_siblings>1){printk(KERN_WARNING"WARNING: polling idle and HT enabled,"" performance may degrade.\n");}#endif-if(pm_idle)+if(local_idle)return;if(cpu_has(c,X86_FEATURE_MWAIT)&&mwait_usable(c)){
@@ -534,18 +571,20 @@ void __cpuinit select_idle_routine(const*OneCPUsupportsmwait=>AllCPUssupportsmwait*/printk(KERN_INFO"using mwait in idle threads.\n");-pm_idle=mwait_idle;+local_idle=mwait_idle;}elseif(check_c1e_idle(c)){printk(KERN_INFO"using C1E aware idle routine\n");-pm_idle=c1e_idle;+local_idle=c1e_idle;}else-pm_idle=default_idle;+local_idle=default_idle;++return;}void__initinit_c1e_mask(void){/* If we're using c1e_idle, we need to allocate c1e_mask. */-if(pm_idle==c1e_idle)+if(local_idle==c1e_idle)zalloc_cpumask_var(&c1e_mask,GFP_KERNEL);}
@@ -567,7 +606,7 @@ static int __init idle_setup(char *str)*TocontinuetoloadtheCPUidledriver,don'ttouch*theboot_option_idle_override.*/-pm_idle=default_idle;+local_idle=default_idle;idle_halt=1;return0;}elseif(!strcmp(str,"nomwait")){
What guarantees that the cpuidle bits actually select this
cpuidle_default driver when you do idle=poll?
When we do a idle=poll, it sets boot_option_idle_override = 1, which
is checked during cpuidle_register_device in acpi/processor_idle.c
So cpuidle devices are not even registered if this option is set.
But, in acpi/processor_core.c where cpuidle_register_driver happens,
this check is not made currently. So, I guess this check must be added
before we register acpi_idle driver.
Also, cpuidle already has a poll loop in it, why duplicate that?
Suppose the arch doesnt have a poll loop of its own, it can use the
one provided by cpuidle. I have just retained this from the earlier
implementation.
--arun
@@ -244,12 +245,6 @@ int sys_vfork(struct pt_regs *regs)unsignedlongboot_option_idle_override=0;EXPORT_SYMBOL(boot_option_idle_override);-/*-*Powermanagementidlefunction,ifany..-*/-void(*pm_idle)(void);-EXPORT_SYMBOL(pm_idle);-#ifdef CONFIG_X86_32/**ThishaltmagicwasaworkaroundforancientfloppyDMA
@@ -329,17 +324,15 @@ static void do_nothing(void *unused)}/*-*cpu_idle_wait-UsedtoensurethatalltheCPUsdiscardoldvalueof-*pm_idleandupdatetonewpm_idlevalue.Requiredwhilechangingpm_idle-*handleronSMPsystems.+*cpu_idle_wait-RequiredwhilechangingidleroutinehandleronSMPsystems.*-*Callermusthavechangedpm_idletothenewvaluebeforethecall.Old-*pm_idlevaluewillnotbeusedbyanyCPUafterthereturnofthisfunction.+*Callermusthavechangedidleroutinetothenewvaluebeforethecall.Old+*valuewillnotbeusedbyanyCPUafterthereturnofthisfunction.*/voidcpu_idle_wait(void){smp_mb();-/* kick all the CPUs so that they exit out of pm_idle */+/* kick all the CPUs so that they exit out of idle loop */smp_call_function(do_nothing,NULL,1);}EXPORT_SYMBOL_GPL(cpu_idle_wait);
@@ -518,15 +511,59 @@ static void c1e_idle(void)default_idle();}+staticvoid(*local_idle)(void);+DEFINE_PER_CPU(structcpuidle_device,idle_devices);++structcpuidle_drivercpuidle_default_driver={+.name="cpuidle_default",+};++staticintlocal_idle_loop(structcpuidle_device*dev,structcpuidle_state*st)+{+ktime_tt1,t2;+s64diff;+intret;++t1=ktime_get();+local_idle();+t2=ktime_get();++diff=ktime_to_us(ktime_sub(t2,t1));+if(diff>INT_MAX)+diff=INT_MAX;+ret=(int)diff;++returnret;+}++staticintsetup_cpuidle_simple(void)+{+structcpuidle_device*dev;+intcpu;++if(!cpuidle_curr_driver)+cpuidle_register_driver(&cpuidle_default_driver);++for_each_online_cpu(cpu){+dev=&per_cpu(idle_devices,cpu);+dev->cpu=cpu;+dev->states[0].enter=local_idle_loop;+dev->state_count=1;+cpuidle_register_device(dev);+}+return0;+}+device_initcall(setup_cpuidle_simple);+void__cpuinitselect_idle_routine(conststructcpuinfo_x86*c){#ifdef CONFIG_SMP-if(pm_idle==poll_idle&&smp_num_siblings>1){+if(local_idle==poll_idle&&smp_num_siblings>1){printk(KERN_WARNING"WARNING: polling idle and HT enabled,"" performance may degrade.\n");}#endif-if(pm_idle)+if(local_idle)return;if(cpu_has(c,X86_FEATURE_MWAIT)&&mwait_usable(c)){
@@ -534,18 +571,20 @@ void __cpuinit select_idle_routine(const*OneCPUsupportsmwait=>AllCPUssupportsmwait*/printk(KERN_INFO"using mwait in idle threads.\n");-pm_idle=mwait_idle;+local_idle=mwait_idle;}elseif(check_c1e_idle(c)){printk(KERN_INFO"using C1E aware idle routine\n");-pm_idle=c1e_idle;+local_idle=c1e_idle;}else-pm_idle=default_idle;+local_idle=default_idle;++return;}void__initinit_c1e_mask(void){/* If we're using c1e_idle, we need to allocate c1e_mask. */-if(pm_idle==c1e_idle)+if(local_idle==c1e_idle)zalloc_cpumask_var(&c1e_mask,GFP_KERNEL);}
@@ -567,7 +606,7 @@ static int __init idle_setup(char *str)*TocontinuetoloadtheCPUidledriver,don'ttouch*theboot_option_idle_override.*/-pm_idle=default_idle;+local_idle=default_idle;idle_halt=1;return0;}elseif(!strcmp(str,"nomwait")){
What guarantees that the cpuidle bits actually select this
cpuidle_default driver when you do idle=poll?
Also, cpuidle already has a poll loop in it, why duplicate that?
Yes, now i see it.. I'll get rid of the redundant poll_idle definition