From: Arun R Bharadwaj <hidden> Date: 2009-09-01 11:37:19
Hi,
******** This is an RFC, not for inclusion **********
This patchset introduces cpuidle infrastructure to POWER, prototyping
for pseries and currently in the process of porting to x86 and hence
will *not* build on x86/other POWER platforms.
This is to get initial comments on the redesign of my earlier implementation
which can be found at http://lkml.org/lkml/2009/8/27/124
Major changes from last iteration:
----------------------------------
* Cleanup drivers/cpuidle/cpuidle.c
Currently, the cpuidle implementation has weakness in the
framework where an exported pm_idle function pointer is
manipulated by various subsystem. The proposed framework has
a registration architecture to cleanly add and remove new idle
routines from different subsystems.
* Introduce [un]register_idle_function() routines
Implement a LIFO based approach for registering architecture
dependent idle routines.
* Sample implementation of register_idle_function for pSeries
TODO:
-----
* Extend this prototype to cover x86 and other archs that use cpuidle.
Currently, in x86, the cpu_idle() idle loop doesn't have a
default idle loop to fall back to if pm_idle is NULL, unlike
the corresponding implementation in pseries, where
ppc_md.power_save can be NULL and there is a fallback.
So we need to create a similar fork in cpu_idle() idle loop of
x86.
Patches included in this series:
--------------------------------
1/5 - Cleanup drivers/cpuidle/cpuidle.c
2/5 - Implement routines to register and unregister idle function.
3/5 - Incorporate registering of idle loop for pSeries.
4/5 - Add Kconfig entry to enable cpuidle for POWER.
5/5 - Implement pSeries processor idle module.
Any comments on the design is welcome.
--arun
From: Arun R Bharadwaj <hidden> Date: 2009-09-01 11:38:50
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
Cleanup drivers/cpuidle/cpuidle.c
Cpuidle maintains a pm_idle_old void pointer because, currently in x86
there is no clean way of registering and unregistering a idle function.
So remove pm_idle_old and leave the responsibility of maintaining the
list of registered idle loops to the architecture specific code. If the
architecture registers cpuidle_idle_call as its idle loop, only then
this loop is called.
Also remove unwanted functions cpuidle_[un]install_idle_handler,
cpuidle_kick_cpus()
Signed-off-by: Arun R Bharadwaj <redacted>
---
drivers/cpuidle/cpuidle.c | 51 +++++++++++++++------------------------------
drivers/cpuidle/governor.c | 3 --
2 files changed, 17 insertions(+), 37 deletions(-)
Index: linux.trees.git/drivers/cpuidle/cpuidle.c
===================================================================
@@ -54,13 +59,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;}
@@ -94,35 +96,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);
@@ -48,8 +48,6 @@ int cpuidle_switch_governor(struct cpuidif(gov==cpuidle_curr_governor)return0;-cpuidle_uninstall_idle_handler();-if(cpuidle_curr_governor){list_for_each_entry(dev,&cpuidle_detected_devices,device_list)cpuidle_disable_device(dev);
@@ -63,7 +61,6 @@ int cpuidle_switch_governor(struct cpuidreturn-EINVAL;list_for_each_entry(dev,&cpuidle_detected_devices,device_list)cpuidle_enable_device(dev);-cpuidle_install_idle_handler();printk(KERN_INFO"cpuidle: using governor %s\n",gov->name);}
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
Cleanup drivers/cpuidle/cpuidle.c
Cpuidle maintains a pm_idle_old void pointer because, currently in x86
there is no clean way of registering and unregistering a idle function.
So remove pm_idle_old and leave the responsibility of maintaining the
list of registered idle loops to the architecture specific code. If the
architecture registers cpuidle_idle_call as its idle loop, only then
this loop is called.
It sounds as if there is a side-effect of this
patch on x86 (am I reading it incorrectly), which can be fixed, but
it will need a patch or so to get back the old behaviour on x86.
@@ -54,13 +59,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;}
@@ -94,35 +96,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);
@@ -48,8 +48,6 @@ int cpuidle_switch_governor(struct cpuidif(gov==cpuidle_curr_governor)return0;-cpuidle_uninstall_idle_handler();-if(cpuidle_curr_governor){list_for_each_entry(dev,&cpuidle_detected_devices,device_list)cpuidle_disable_device(dev);
@@ -63,7 +61,6 @@ int cpuidle_switch_governor(struct cpuidreturn-EINVAL;list_for_each_entry(dev,&cpuidle_detected_devices,device_list)cpuidle_enable_device(dev);-cpuidle_install_idle_handler();printk(KERN_INFO"cpuidle: using governor %s\n",gov->name);}
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
Cleanup drivers/cpuidle/cpuidle.c
Cpuidle maintains a pm_idle_old void pointer because, currently in x86
there is no clean way of registering and unregistering a idle function.
So remove pm_idle_old and leave the responsibility of maintaining the
list of registered idle loops to the architecture specific code. If the
architecture registers cpuidle_idle_call as its idle loop, only then
this loop is called.
It sounds as if there is a side-effect of this
patch on x86 (am I reading it incorrectly), which can be fixed, but
it will need a patch or so to get back the old behaviour on x86.
Hi Balbir,
Yes, your understanding is correct. Currently, x86 exports pm_idle and
this pm_idle is set to cpuidle_idle_call inside cpuidle.c
So instead of that x86 should just export a function called
set_arch_idle() which will be called from within
register_idle_function() and set pm_idle to the idle handler which is
currently being registered.
I have implemented this for pseries, and in the process of doing it
for x86 too.
@@ -54,13 +59,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;}
@@ -94,35 +96,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);
What does this mean for users of cpuidle_pause_and_lock/unlock?
Should we be calling register/unregister_idle_function here?
Yes, you are right. I have missed out on this part.
register/unregister_idle_function should replace
install/uninstall_idle_handler at those places. Thanks.
@@ -48,8 +48,6 @@ int cpuidle_switch_governor(struct cpuidif(gov==cpuidle_curr_governor)return0;-cpuidle_uninstall_idle_handler();-if(cpuidle_curr_governor){list_for_each_entry(dev,&cpuidle_detected_devices,device_list)cpuidle_disable_device(dev);
@@ -63,7 +61,6 @@ int cpuidle_switch_governor(struct cpuidreturn-EINVAL;list_for_each_entry(dev,&cpuidle_detected_devices,device_list)cpuidle_enable_device(dev);-cpuidle_install_idle_handler();printk(KERN_INFO"cpuidle: using governor %s\n",gov->name);}
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
Cleanup drivers/cpuidle/cpuidle.c
Cpuidle maintains a pm_idle_old void pointer because, currently in x86
there is no clean way of registering and unregistering a idle function.
So remove pm_idle_old and leave the responsibility of maintaining the
list of registered idle loops to the architecture specific code. If the
architecture registers cpuidle_idle_call as its idle loop, only then
this loop is called.
It sounds as if there is a side-effect of this
patch on x86 (am I reading it incorrectly), which can be fixed, but
it will need a patch or so to get back the old behaviour on x86.
@@ -54,13 +59,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;}
@@ -94,35 +96,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);
What does this mean for users of cpuidle_pause_and_lock/unlock?
Should we be calling register/unregister_idle_function here?
Just observed the use case for cpuidle_pause_and_lock/unlock.
It is not clear as to why we need to switch back to the old idle
handler and then again back to cpuidle's idle handler. Wouldn't it
make more sense to just register the idle handler when the first
cpuidle device is being registered and unregister the idle handler
when the last cpuidle device is unregistered?
--arun
@@ -48,8 +48,6 @@ int cpuidle_switch_governor(struct cpuidif(gov==cpuidle_curr_governor)return0;-cpuidle_uninstall_idle_handler();-if(cpuidle_curr_governor){list_for_each_entry(dev,&cpuidle_detected_devices,device_list)cpuidle_disable_device(dev);
@@ -63,7 +61,6 @@ int cpuidle_switch_governor(struct cpuidreturn-EINVAL;list_for_each_entry(dev,&cpuidle_detected_devices,device_list)cpuidle_enable_device(dev);-cpuidle_install_idle_handler();printk(KERN_INFO"cpuidle: using governor %s\n",gov->name);}
From: Peter Zijlstra <hidden> Date: 2009-09-02 05:42:40
On Tue, 2009-09-01 at 17:08 +0530, Arun R Bharadwaj wrote:
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
Cleanup drivers/cpuidle/cpuidle.c
Cpuidle maintains a pm_idle_old void pointer because, currently in x86
there is no clean way of registering and unregistering a idle function.
Right, and instead of fixing that, they build this cpuidle crap on top,
instead of replacing the current crap with it.
So remove pm_idle_old and leave the responsibility of maintaining the
list of registered idle loops to the architecture specific code. If the
architecture registers cpuidle_idle_call as its idle loop, only then
this loop is called.
OK, that's a start I guess. Best would be to replace all of pm_idle with
cpuidle, which is what should have been done from the very start.
If cpuidle cannot fully replace the pm_idle functionality, then it needs
to fix that. But having two layers of idle functions is just silly.
Looking at patch 2 and 3, you're making the same mistake on power, after
those patches there are multiple ways of registering idle functions, one
through some native interface and one through cpuidle, this strikes me
as undesirable.
If cpuidle is a good idle function manager, then it should be good
enough to be the sole one, if its not, then why bother with it at all.
From: Arun R Bharadwaj <hidden> Date: 2009-09-03 04:43:05
* Peter Zijlstra [off-list ref] [2009-09-02 07:42:24]:
On Tue, 2009-09-01 at 17:08 +0530, Arun R Bharadwaj wrote:
quoted
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
Cleanup drivers/cpuidle/cpuidle.c
Cpuidle maintains a pm_idle_old void pointer because, currently in x86
there is no clean way of registering and unregistering a idle function.
Right, and instead of fixing that, they build this cpuidle crap on top,
instead of replacing the current crap with it.
quoted
So remove pm_idle_old and leave the responsibility of maintaining the
list of registered idle loops to the architecture specific code. If the
architecture registers cpuidle_idle_call as its idle loop, only then
this loop is called.
OK, that's a start I guess. Best would be to replace all of pm_idle with
cpuidle, which is what should have been done from the very start.
If cpuidle cannot fully replace the pm_idle functionality, then it needs
to fix that. But having two layers of idle functions is just silly.
Looking at patch 2 and 3, you're making the same mistake on power, after
those patches there are multiple ways of registering idle functions, one
through some native interface and one through cpuidle, this strikes me
as undesirable.
If cpuidle is a good idle function manager, then it should be good
enough to be the sole one, if its not, then why bother with it at all.
Okay, I'm giving this approach a shot now. i.e. trying to make cpuidle
as _the_ sole idle function manager. This would mean doing away with
pm_idle and ppc_md.power_save. And, cpuidle_idle_call() which is the
main idle loop of cpuidle, present in drivers/cpuidle/cpuidle.c will
have to be called from arch specific code of cpu_idle()
Also this would mean enabling cpuidle for all platforms, even if the
platform doesn't have multiple idle states. So suppose a platform doesnt
have multiple states, it wouldn't want the bloated code of cpuidle
governors, and would want just a simple cpuidle loop.
--arun
From: Peter Zijlstra <hidden> Date: 2009-09-03 09:40:44
On Thu, 2009-09-03 at 10:12 +0530, Arun R Bharadwaj wrote:
quoted
OK, that's a start I guess. Best would be to replace all of pm_idle with
cpuidle, which is what should have been done from the very start.
If cpuidle cannot fully replace the pm_idle functionality, then it needs
to fix that. But having two layers of idle functions is just silly.
Looking at patch 2 and 3, you're making the same mistake on power, after
those patches there are multiple ways of registering idle functions, one
through some native interface and one through cpuidle, this strikes me
as undesirable.
If cpuidle is a good idle function manager, then it should be good
enough to be the sole one, if its not, then why bother with it at all.
Okay, I'm giving this approach a shot now. i.e. trying to make cpuidle
as _the_ sole idle function manager. This would mean doing away with
pm_idle and ppc_md.power_save. And, cpuidle_idle_call() which is the
main idle loop of cpuidle, present in drivers/cpuidle/cpuidle.c will
have to be called from arch specific code of cpu_idle()
Also this would mean enabling cpuidle for all platforms, even if the
platform doesn't have multiple idle states. So suppose a platform doesnt
have multiple states, it wouldn't want the bloated code of cpuidle
governors, and would want just a simple cpuidle loop.
Do talk to the powerpc maintainers about this. But yes, something like
that should be doable.
AFAICT the whole governor thing is optional and cpuidle provides a
spinning idle loop by default, and platforms can always register a
simple alternative when they set up bits -- the only thing to be careful
about is not creating a chicken-egg problem where the platform setup
runs before cpuidle is able to register a new handler or something.
I'd be delighted to see the end of pm_idle on x86.
From: Arun R Bharadwaj <hidden> Date: 2009-09-01 11:39:46
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
Implement a LIFO based approach for registering arch dependent
idle routines.
This is a prototype for pseries, needs to be extended
for other platforms.
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/powerpc/kernel/idle.c | 5 +++++
drivers/cpuidle/cpuidle.c | 37 +++++++++++++++++++++++++++++++++++++
include/linux/pm.h | 10 ++++++++++
3 files changed, 52 insertions(+)
Index: linux.trees.git/arch/powerpc/kernel/idle.c
===================================================================
From: Arun R Bharadwaj <hidden> Date: 2009-09-01 11:40:41
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
Platform needs to register its idle function via register_idle_function()
in order to provide a clean way of handling the ppc_md.power_save
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/powerpc/platforms/pseries/setup.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
Index: linux.trees.git/arch/powerpc/platforms/pseries/setup.c
===================================================================
From: Arun R Bharadwaj <hidden> Date: 2009-09-01 11:41:37
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
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
@@ -107,6 +107,25 @@ void cpu_idle(void)}}+staticvoiddo_nothing(void*unused)+{+}++/*+*cpu_idle_wait-UsedtoensurethatalltheCPUsdiscardoldvalueof+*ppc_md.power_saveandupdatetonewvalue.+*Requiredwhilechangingppc_md.power_savehandleronSMPsystems.+*Callermusthavechangedppc_md.power_savetothenewvaluebeforethecall.+*/+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-09-01 11:43:04
* Arun R Bharadwaj [off-list ref] [2009-09-01 17:07:04]:
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 snooze or nap
state based on the decision taken by the cpuidle governor.
Signed-off-by: Arun R Bharadwaj <redacted>
---
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/processor_idle.c | 179 ++++++++++++++++++++++++
arch/powerpc/platforms/pseries/pseries.h | 14 +
arch/powerpc/platforms/pseries/setup.c | 3
4 files changed, 194 insertions(+), 3 deletions(-)
Index: linux.trees.git/arch/powerpc/platforms/pseries/Makefile
===================================================================