ACPI, CPU_IDLE: ACPI processor_idle changes, C1 residency time, etc
cover ACPI, CPU_IDLE: ACPI processor_idle changes, C1 residency time, etc
ACPI and CPU_IDLE patch series. Individual patches has more details.
PATCH 1/4 ACPI: Fix acpi_safe_halt usages and interrupt enabling/disabling
acpi_safe_halt() needs interrupts to be disabled for atomic need_resched check and safe halt. Otherwise we may miss an interrupt and go into halt.
acpi_safe_halt() also does not enable interrupts on all return paths.
So the callers should handle enable and disable interrupts around it.
There were multiple places where this was not happening. Below is the fix.
drivers/acpi/processor_idle.c +11 -0
@@ -201,6 +201,10 @@ static inline u32 ticks_elapsed_in_us(u3return PM_TIMER_TICKS_TO_US ((0xFFFFFFFF - t1) + t2); } +/* +* Callers should disable interrupts before the call and enable +* interrupts after return. +*/ static void acpi_safe_halt (void ) {current_thread_info ()->status &= ~TS_POLLING ;
@@ -393,6 +397,8 @@ static void acpi_processor_idle(void)pm_idle_save (); elseacpi_safe_halt (); + +local_irq_enable ();return ; }
@@ -501,6 +507,7 @@ static void acpi_processor_idle(void)* skew otherwise. */ sleep_ticks =0xFFFFFFFF ; +local_irq_enable ();break ;case ACPI_STATE_C2 :
@@ -1378,11 +1385,13 @@ static int acpi_idle_enter_c1(struct cpuif (unlikely (!pr))return 0 ; +local_irq_disable ();if (pr->flags.bm_check)acpi_idle_update_bm_rld (pr, cx);acpi_safe_halt (); +local_irq_enable (); cx->usage++;return 0 ;
@@ -1490,7 +1499,9 @@ static int acpi_idle_enter_bm(struct cpuif (dev->safe_state) {return dev->safe_state->enter (dev, dev->safe_state); }else { +local_irq_disable ();acpi_safe_halt (); +local_irq_enable ();return 0 ; } }
Re: [patch 1/4] ACPI: Fix acpi_safe_halt usages and interrupt enabling/disabling
applied.
thanks,
-len
Quoted 69 lines · On Thursday 31 January 2008 20:35, [email protected] wrote:
acpi_safe_halt()needs interrupts to be disabled for atomicneed_reschedcheck and safe halt. Otherwise we may miss an interrupt and go into halt.
acpi_safe_halt()also does not enable interrupts on all return paths.So the callers should handle enable and disable interrupts around it.
There were multiple places where this was not happening. Below is the fix.
Signed-off-by Venkatesh Pallipadi <[email protected]>Index:
linux-2.6.25-rc/drivers/acpi/processor_idle.c
===================================================================drivers/acpi/processor_idle.c +11
-0@@ -201,6 +201,10 @@ static inline u32 ticks_elapsed_in_us(u3return PM_TIMER_TICKS_TO_US ((0xFFFFFFFF - t1) + t2); } +/* +* Callers should disable interrupts before the call and enable +* interrupts after return. +*/ static void acpi_safe_halt (void ) {current_thread_info ()->status &= ~TS_POLLING ;@@ -393,6 +397,8 @@ static void acpi_processor_idle(void)pm_idle_save (); elseacpi_safe_halt (); + +local_irq_enable ();return ; }@@ -501,6 +507,7 @@ static void acpi_processor_idle(void)* skew otherwise. */ sleep_ticks =0xFFFFFFFF ; +local_irq_enable ();break ;case ACPI_STATE_C2 :@@ -1378,11 +1385,13 @@ static int acpi_idle_enter_c1(struct cpuif (unlikely (!pr))return 0 ; +local_irq_disable ();if (pr->flags.bm_check)acpi_idle_update_bm_rld (pr, cx);acpi_safe_halt (); +local_irq_enable (); cx->usage++;return 0 ;@@ -1490,7 +1499,9 @@ static int acpi_idle_enter_bm(struct cpuif (dev->safe_state) {return dev->safe_state->enter (dev, dev->safe_state); }else { +local_irq_disable ();acpi_safe_halt (); +local_irq_enable ();return 0 ; } }
PATCH 2/4 ACPI: Use mwait for C1 idle
In reply to [email protected]Add mwait idle for C1 state instead of halt, on platforms that support C1 state with mwait.
Renames cx->space_id to something more appropriate.
drivers/acpi/processor_idle.c +12 -8
@@ -265,7 +265,7 @@ static atomic_t c3_cpu_count;/* Common C-state entry for C2, C3, .. */ static void acpi_cstate_enter (struct acpi_processor_cx *cstate) { -if (cstate->space_id ==ACPI_CSTATE_FFH ) { +if (cstate->entry_method ==ACPI_CSTATE_FFH ) {/* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter (cstate); }else {
@@ -904,20 +904,20 @@ static int acpi_processor_get_power_info cx.address = reg->address; cx.index = current_count +1 ; - cx.space_id =ACPI_CSTATE_SYSTEMIO ; + cx.entry_method =ACPI_CSTATE_SYSTEMIO ;if (reg->space_id ==ACPI_ADR_SPACE_FIXED_HARDWARE ) {if (acpi_processor_ffh_cstate_probe (pr->id, &cx, reg) ==0 ) { - cx.space_id =ACPI_CSTATE_FFH ; - }else if (cx.type !=ACPI_STATE_C1 ) { + cx.entry_method =ACPI_CSTATE_FFH ; + }else if (cx.type ==ACPI_STATE_C1 ) {/* * C1 is a special case where FIXED_HARDWARE * can be handled in non-MWAIT way as well. * In that case, save this _CST entry info. -* That is, we retain space_id of SYSTEM_IO for -* halt based C1. * Otherwise, ignore this info and continue. */ + cx.entry_method =ACPI_CSTATE_HALT ; + }else {continue ; } }
@@ -1351,12 +1351,16 @@ static inline void acpi_idle_update_bm_r/** * acpi_idle_do_entry - a helper function that does C2 and C3 type entry * @cx: cstate data +* +* Caller disables interrupt before call and enables interrupt after return. */ static inline void acpi_idle_do_entry (struct acpi_processor_cx *cx) { -if (cx->space_id ==ACPI_CSTATE_FFH ) { +if (cx->entry_method ==ACPI_CSTATE_FFH ) {/* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter (cx); + }else if (cx->entry_method ==ACPI_CSTATE_HALT ) { +acpi_safe_halt (); }else {int unused;/* IO port based C-state */
@@ -1389,7 +1393,7 @@ static int acpi_idle_enter_c1(struct cpuif (pr->flags.bm_check)acpi_idle_update_bm_rld (pr, cx); -acpi_safe_halt (); +acpi_idle_do_entry (cx);local_irq_enable (); cx->usage++;
include/acpi/processor.h +2 -1
@@ -34,6 +34,7 @@#define ACPI_CSTATE_SYSTEMIO (0)#define ACPI_CSTATE_FFH (1) +#define ACPI_CSTATE_HALT (2)/* Power Management */
@@ -64,7 +65,7 @@ struct acpi_processor_cx {u8 valid;u8 type;u32 address; -u8 space_id; +u8 entry_method;u8 index;u32 latency;u32 latency_ticks;
Re: [patch 2/4] ACPI: Use mwait for C1 idle
applied.
thanks,
-len
Quoted 95 lines · On Thursday 31 January 2008 20:35, [email protected] wrote:
Add mwait idle for C1 state instead of halt, on platforms that support C1 state with mwait.
Renames
cx->space_idto something more appropriate.Signed-off-by Venkatesh Pallipadi <[email protected]>Index:
linux-2.6.25-rc/drivers/acpi/processor_idle.c
===================================================================drivers/acpi/processor_idle.c +12
-8@@ -265,7 +265,7 @@ static atomic_t c3_cpu_count;/* Common C-state entry for C2, C3, .. */ static void acpi_cstate_enter (struct acpi_processor_cx *cstate) { -if (cstate->space_id ==ACPI_CSTATE_FFH ) { +if (cstate->entry_method ==ACPI_CSTATE_FFH ) {/* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter (cstate); }else {@@ -904,20 +904,20 @@ static int acpi_processor_get_power_info cx.address = reg->address; cx.index = current_count +1 ; - cx.space_id =ACPI_CSTATE_SYSTEMIO ; + cx.entry_method =ACPI_CSTATE_SYSTEMIO ;if (reg->space_id ==ACPI_ADR_SPACE_FIXED_HARDWARE ) {if (acpi_processor_ffh_cstate_probe (pr->id, &cx, reg) ==0 ) { - cx.space_id =ACPI_CSTATE_FFH ; - }else if (cx.type !=ACPI_STATE_C1 ) { + cx.entry_method =ACPI_CSTATE_FFH ; + }else if (cx.type ==ACPI_STATE_C1 ) {/* * C1 is a special case where FIXED_HARDWARE * can be handled in non-MWAIT way as well. * In that case, save this _CST entry info. -* That is, we retain space_id of SYSTEM_IO for -* halt based C1. * Otherwise, ignore this info and continue. */ + cx.entry_method =ACPI_CSTATE_HALT ; + }else {continue ; } }@@ -1351,12 +1351,16 @@ static inline void acpi_idle_update_bm_r/** * acpi_idle_do_entry - a helper function that does C2 and C3 type entry * @cx: cstate data +* +* Caller disables interrupt before call and enables interrupt after return. */ static inline void acpi_idle_do_entry (struct acpi_processor_cx *cx) { -if (cx->space_id ==ACPI_CSTATE_FFH ) { +if (cx->entry_method ==ACPI_CSTATE_FFH ) {/* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter (cx); + }else if (cx->entry_method ==ACPI_CSTATE_HALT ) { +acpi_safe_halt (); }else {int unused;/* IO port based C-state */ @@ -1389,7 +1393,7 @@ static int acpi_idle_enter_c1(struct cpuif (pr->flags.bm_check)acpi_idle_update_bm_rld (pr, cx); -acpi_safe_halt (); +acpi_idle_do_entry (cx);local_irq_enable (); cx->usage++;Index:
linux-2.6.25-rc/include/acpi/processor.h
===================================================================include/acpi/processor.h +2
-1@@ -34,6 +34,7 @@#define ACPI_CSTATE_SYSTEMIO (0)#define ACPI_CSTATE_FFH (1) +#define ACPI_CSTATE_HALT (2)/* Power Management */ @@ -64,7 +65,7 @@ struct acpi_processor_cx {u8 valid;u8 type;u32 address; -u8 space_id; +u8 entry_method;u8 index;u32 latency;u32 latency_ticks;
PATCH 3/4 ACPI, CPU_IDLE: Support C1 idle time accounting
In reply to [email protected]Show C1 idle time in /sysfs cpuidle interface. C1 idle time may not be entirely accurate in all cases. It includes the time spent in the interrupt handler after wakeup with "hlt" based C1. But, it will be accurate with "mwait" based C1.
drivers/acpi/processor_idle.c +6 -1
@@ -1382,8 +1382,10 @@ static inline void acpi_idle_do_entry(ststatic int acpi_idle_enter_c1 (struct cpuidle_device *dev,struct cpuidle_state *state) { +u32 t1, t2;struct acpi_processor *pr;struct acpi_processor_cx *cx =cpuidle_get_statedata (state); + pr = processors[smp_processor_id ()];if (unlikely (!pr))
@@ -1393,12 +1395,14 @@ static int acpi_idle_enter_c1(struct cpuif (pr->flags.bm_check)acpi_idle_update_bm_rld (pr, cx); + t1 =inl (acpi_gbl_FADT.xpm_timer_block.address);acpi_idle_do_entry (cx); + t2 =inl (acpi_gbl_FADT.xpm_timer_block.address);local_irq_enable (); cx->usage++; -return 0 ; +return ticks_elapsed_in_us (t1, t2); }/**
@@ -1632,6 +1636,7 @@ static int acpi_processor_setup_cpuidle(switch (cx->type) {case ACPI_STATE_C1 : state->flags |=CPUIDLE_FLAG_SHALLOW ; + state->flags |=CPUIDLE_FLAG_TIME_VALID ; state->enter = acpi_idle_enter_c1; dev->safe_state = state;break ;
Re: [patch 3/4] ACPI, CPU_IDLE: Support C1 idle time accounting
applied.
thanks,
-len
Quoted 47 lines · On Thursday 31 January 2008 20:35, [email protected] wrote:
Show C1 idle time in /sysfs cpuidle interface. C1 idle time may not be entirely accurate in all cases. It includes the time spent in the interrupt handler after wakeup with "hlt" based C1. But, it will be accurate with "mwait" based C1.
Signed-off-by Venkatesh Pallipadi <[email protected]>Index:
linux-2.6.25-rc/drivers/acpi/processor_idle.c
===================================================================drivers/acpi/processor_idle.c +6
-1@@ -1382,8 +1382,10 @@ static inline void acpi_idle_do_entry(ststatic int acpi_idle_enter_c1 (struct cpuidle_device *dev,struct cpuidle_state *state) { +u32 t1, t2;struct acpi_processor *pr;struct acpi_processor_cx *cx =cpuidle_get_statedata (state); + pr = processors[smp_processor_id ()];if (unlikely (!pr))@@ -1393,12 +1395,14 @@ static int acpi_idle_enter_c1(struct cpuif (pr->flags.bm_check)acpi_idle_update_bm_rld (pr, cx); + t1 =inl (acpi_gbl_FADT.xpm_timer_block.address);acpi_idle_do_entry (cx); + t2 =inl (acpi_gbl_FADT.xpm_timer_block.address);local_irq_enable (); cx->usage++; -return 0 ; +return ticks_elapsed_in_us (t1, t2); }/** @@ -1632,6 +1636,7 @@ static int acpi_processor_setup_cpuidle(switch (cx->type) {case ACPI_STATE_C1 : state->flags |=CPUIDLE_FLAG_SHALLOW ; + state->flags |=CPUIDLE_FLAG_TIME_VALID ; state->enter = acpi_idle_enter_c1; dev->safe_state = state;break ;
PATCH 4/4 CPUIDLE: Add a poll_idle method into CPU_IDLE
In reply to [email protected]Add a default poll idle state with 0 latency. Provides an option to users to use poll_idle by using 0 as the latency requirement.
drivers/acpi/processor_idle.c +3 -1
@@ -1600,7 +1600,7 @@ struct cpuidle_driver acpi_idle_driver =*/ static int acpi_processor_setup_cpuidle (struct acpi_processor *pr) { -int i, count =0 ; +int i, count =CPUIDLE_DRIVER_STATE_START ;struct acpi_processor_cx *cx;struct cpuidle_state *state;struct cpuidle_device *dev = &pr->power.dev;
@@ -1659,6 +1659,8 @@ static int acpi_processor_setup_cpuidle( } count++; +if (count ==CPUIDLE_STATE_MAX ) +break ; } dev->state_count = count;
drivers/cpuidle/cpuidle.c +41 -0
59 lines not on this page · raw message
include/linux/cpuidle.h +10 -3
@@ -46,9 +46,10 @@ struct cpuidle_state {/* Idle State Flags */ #define CPUIDLE_FLAG_TIME_VALID (0x01)/* is residency time measurable? */ #define CPUIDLE_FLAG_CHECK_BM (0x02)/* BM activity will exit state */ -#define CPUIDLE_FLAG_SHALLOW (0x10)/* low latency, minimal savings */ -#define CPUIDLE_FLAG_BALANCED (0x20)/* medium latency, moderate savings */ -#define CPUIDLE_FLAG_DEEP (0x40)/* high latency, large savings */ +#define CPUIDLE_FLAG_POLL (0x10)/* no latency, no savings */ +#define CPUIDLE_FLAG_SHALLOW (0x20)/* low latency, minimal savings */ +#define CPUIDLE_FLAG_BALANCED (0x40)/* medium latency, moderate savings */ +#define CPUIDLE_FLAG_DEEP (0x80)/* high latency, large savings */ #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000)
@@ -178,4 +179,10 @@ static inline void cpuidle_unregister_go#endif +#ifdef CONFIG_ARCH_HAS_CPU_RELAX +#define CPUIDLE_DRIVER_STATE_START 1 +#else +#define CPUIDLE_DRIVER_STATE_START 0 +#endif +#endif /* _LINUX_CPUIDLE_H */
arch/x86/Kconfig +3 -0
@@ -112,6 +112,9 @@ config GENERIC_TIME_VSYSCALL bool default X86_64 +config ARCH_HAS_CPU_RELAX + def_bool y + config ARCH_SUPPORTS_OPROFILE bool default y
Re: [patch 4/4] CPUIDLE: Add a poll_idle method into CPU_IDLE
applied,
thanks,
-len
Quoted 137 lines · On Thursday 31 January 2008 20:35, [email protected] wrote:
Add a default poll idle state with 0 latency. Provides an option to users to use
poll_idleby using 0 as the latency requirement.Signed-off-by Venkatesh Pallipadi <[email protected]>Index:
linux-2.6.25-rc/drivers/acpi/processor_idle.c
===================================================================drivers/acpi/processor_idle.c +3
-1@@ -1600,7 +1600,7 @@ struct cpuidle_driver acpi_idle_driver =*/ static int acpi_processor_setup_cpuidle (struct acpi_processor *pr) { -int i, count =0 ; +int i, count =CPUIDLE_DRIVER_STATE_START ;struct acpi_processor_cx *cx;struct cpuidle_state *state;struct cpuidle_device *dev = &pr->power.dev;@@ -1659,6 +1659,8 @@ static int acpi_processor_setup_cpuidle( } count++; +if (count ==CPUIDLE_STATE_MAX ) +break ; } dev->state_count = count;Index:
linux-2.6.25-rc/drivers/cpuidle/cpuidle.c
===================================================================drivers/cpuidle/cpuidle.c +41
-0@@ -15,6 +15,7 @@#include <linux/latency.h> #include <linux/cpu.h> #include <linux/cpuidle.h> +#include <linux/ktime.h> #include "cpuidle.h" @@ -180,6 +181,44 @@ void cpuidle_disable_device(struct cpuidEXPORT_SYMBOL_GPL (cpuidle_disable_device); +#ifdef CONFIG_ARCH_HAS_CPU_RELAX +static int poll_idle (struct cpuidle_device *dev,struct cpuidle_state *st) +{ +ktime_t t1, t2; +s64 diff; +int ret; + + t1 =ktime_get (); +local_irq_enable (); +while (!need_resched ()) { +cpu_relax (); + } + t2 =ktime_get (); + diff =ktime_to_us (ktime_sub (t2, t1)); +if (diff >INT_MAX ) + diff =INT_MAX ; + + ret = (int ) diff; +return ret; +} + +static void poll_idle_init (struct cpuidle_device *dev) +{ +struct cpuidle_state *state = &dev->states[0 ]; + +cpuidle_set_statedata (state,NULL ); + +snprintf (state->name,CPUIDLE_NAME_LEN ,"C0 (poll idle)" ); + state->exit_latency =0 ; + state->target_residency =0 ; + state->power_usage =-1 ; + state->flags =CPUIDLE_FLAG_POLL |CPUIDLE_FLAG_TIME_VALID ; + state->enter = poll_idle; +} +#else +static void poll_idle_init (struct cpuidle_device *dev) {} +#endif /* CONFIG_ARCH_HAS_CPU_RELAX */ +/** * cpuidle_register_device - registers a CPU's idle PM feature * @dev: the cpu @@ -198,6 +237,8 @@ int cpuidle_register_device(struct cpuidmutex_lock (&cpuidle_lock); +poll_idle_init (dev); +per_cpu (cpuidle_devices, dev->cpu) = dev;list_add (&dev->device_list, &cpuidle_detected_devices);if ((ret =cpuidle_add_sysfs (sys_dev))) {Index:
linux-2.6.25-rc/include/linux/cpuidle.h
===================================================================include/linux/cpuidle.h +10
-3@@ -46,9 +46,10 @@ struct cpuidle_state {/* Idle State Flags */ #define CPUIDLE_FLAG_TIME_VALID (0x01)/* is residency time measurable? */ #define CPUIDLE_FLAG_CHECK_BM (0x02)/* BM activity will exit state */ -#define CPUIDLE_FLAG_SHALLOW (0x10)/* low latency, minimal savings */ -#define CPUIDLE_FLAG_BALANCED (0x20)/* medium latency, moderate savings */ -#define CPUIDLE_FLAG_DEEP (0x40)/* high latency, large savings */ +#define CPUIDLE_FLAG_POLL (0x10)/* no latency, no savings */ +#define CPUIDLE_FLAG_SHALLOW (0x20)/* low latency, minimal savings */ +#define CPUIDLE_FLAG_BALANCED (0x40)/* medium latency, moderate savings */ +#define CPUIDLE_FLAG_DEEP (0x80)/* high latency, large savings */ #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000)@@ -178,4 +179,10 @@ static inline void cpuidle_unregister_go#endif +#ifdef CONFIG_ARCH_HAS_CPU_RELAX +#define CPUIDLE_DRIVER_STATE_START 1 +#else +#define CPUIDLE_DRIVER_STATE_START 0 +#endif +#endif /* _LINUX_CPUIDLE_H */ Index: linux-2.6.25-rc/arch/x86/Kconfig
===================================================================arch/x86/Kconfig +3
-0@@ -112,6 +112,9 @@ config GENERIC_TIME_VSYSCALL bool default X86_64 +config ARCH_HAS_CPU_RELAX + def_bool y + config ARCH_SUPPORTS_OPROFILE bool default y