Skip to content

Commit

Permalink
Merge branch 'pm-cpuidle'
Browse files Browse the repository at this point in the history
* pm-cpuidle:
  cpuidle: Measure idle state durations with monotonic clock
  cpuidle: fix a suspicious RCU usage in menu governor
  cpuidle: support multiple drivers
  cpuidle: prepare the cpuidle core to handle multiple drivers
  cpuidle: move driver checking within the lock section
  cpuidle: move driver's refcount to cpuidle
  cpuidle: fixup device.h header in cpuidle.h
  cpuidle / sysfs: move structure declaration into the sysfs.c file
  cpuidle: Get typical recent sleep interval
  cpuidle: Set residency to 0 if target Cstate not enter
  cpuidle: Quickly notice prediction failure in general case
  cpuidle: Quickly notice prediction failure for repeat mode
  cpuidle / sysfs: move kobj initialization in the syfs file
  cpuidle / sysfs: change function parameter
  • Loading branch information
rafaeljw committed Nov 29, 2012
2 parents aa84950 + a474a51 commit 9ee71f5
Show file tree
Hide file tree
Showing 12 changed files with 578 additions and 177 deletions.
4 changes: 2 additions & 2 deletions arch/powerpc/platforms/pseries/processor_idle.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ static struct cpuidle_state *cpuidle_state_table;
static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
{

*kt_before = ktime_get_real();
*kt_before = ktime_get();
*in_purr = mfspr(SPRN_PURR);
/*
* Indicate to the HV that we are idle. Now would be
Expand All @@ -50,7 +50,7 @@ static inline s64 idle_loop_epilog(unsigned long in_purr, ktime_t kt_before)
get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
get_lppaca()->idle = 0;

return ktime_to_us(ktime_sub(ktime_get_real(), kt_before));
return ktime_to_us(ktime_sub(ktime_get(), kt_before));
}

static int snooze_loop(struct cpuidle_device *dev,
Expand Down
57 changes: 3 additions & 54 deletions drivers/acpi/processor_idle.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,31 +735,18 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
static int acpi_idle_enter_c1(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
ktime_t kt1, kt2;
s64 idle_time;
struct acpi_processor *pr;
struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);

pr = __this_cpu_read(processors);
dev->last_residency = 0;

if (unlikely(!pr))
return -EINVAL;

local_irq_disable();


lapic_timer_state_broadcast(pr, cx, 1);
kt1 = ktime_get_real();
acpi_idle_do_entry(cx);
kt2 = ktime_get_real();
idle_time = ktime_to_us(ktime_sub(kt2, kt1));

/* Update device last_residency*/
dev->last_residency = (int)idle_time;

local_irq_enable();
lapic_timer_state_broadcast(pr, cx, 0);

return index;
Expand Down Expand Up @@ -806,19 +793,12 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
struct acpi_processor *pr;
struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
ktime_t kt1, kt2;
s64 idle_time_ns;
s64 idle_time;

pr = __this_cpu_read(processors);
dev->last_residency = 0;

if (unlikely(!pr))
return -EINVAL;

local_irq_disable();


if (cx->entry_method != ACPI_CSTATE_FFH) {
current_thread_info()->status &= ~TS_POLLING;
/*
Expand All @@ -829,7 +809,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,

if (unlikely(need_resched())) {
current_thread_info()->status |= TS_POLLING;
local_irq_enable();
return -EINVAL;
}
}
Expand All @@ -843,22 +822,12 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
if (cx->type == ACPI_STATE_C3)
ACPI_FLUSH_CPU_CACHE();

kt1 = ktime_get_real();
/* Tell the scheduler that we are going deep-idle: */
sched_clock_idle_sleep_event();
acpi_idle_do_entry(cx);
kt2 = ktime_get_real();
idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1));
idle_time = idle_time_ns;
do_div(idle_time, NSEC_PER_USEC);

/* Update device last_residency*/
dev->last_residency = (int)idle_time;
sched_clock_idle_wakeup_event(0);

/* Tell the scheduler how much we idled: */
sched_clock_idle_wakeup_event(idle_time_ns);

local_irq_enable();
if (cx->entry_method != ACPI_CSTATE_FFH)
current_thread_info()->status |= TS_POLLING;

Expand All @@ -883,13 +852,8 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
struct acpi_processor *pr;
struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
ktime_t kt1, kt2;
s64 idle_time_ns;
s64 idle_time;


pr = __this_cpu_read(processors);
dev->last_residency = 0;

if (unlikely(!pr))
return -EINVAL;
Expand All @@ -899,16 +863,11 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
return drv->states[drv->safe_state_index].enter(dev,
drv, drv->safe_state_index);
} else {
local_irq_disable();
acpi_safe_halt();
local_irq_enable();
return -EBUSY;
}
}

local_irq_disable();


if (cx->entry_method != ACPI_CSTATE_FFH) {
current_thread_info()->status &= ~TS_POLLING;
/*
Expand All @@ -919,7 +878,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,

if (unlikely(need_resched())) {
current_thread_info()->status |= TS_POLLING;
local_irq_enable();
return -EINVAL;
}
}
Expand All @@ -934,7 +892,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
*/
lapic_timer_state_broadcast(pr, cx, 1);

kt1 = ktime_get_real();
/*
* disable bus master
* bm_check implies we need ARB_DIS
Expand Down Expand Up @@ -965,18 +922,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
c3_cpu_count--;
raw_spin_unlock(&c3_lock);
}
kt2 = ktime_get_real();
idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1));
idle_time = idle_time_ns;
do_div(idle_time, NSEC_PER_USEC);

/* Update device last_residency*/
dev->last_residency = (int)idle_time;

/* Tell the scheduler how much we idled: */
sched_clock_idle_wakeup_event(idle_time_ns);
sched_clock_idle_wakeup_event(0);

local_irq_enable();
if (cx->entry_method != ACPI_CSTATE_FFH)
current_thread_info()->status |= TS_POLLING;

Expand All @@ -987,6 +935,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
struct cpuidle_driver acpi_idle_driver = {
.name = "acpi_idle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
};

/**
Expand Down
9 changes: 9 additions & 0 deletions drivers/cpuidle/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ config CPU_IDLE

If you're using an ACPI-enabled platform, you should say Y here.

config CPU_IDLE_MULTIPLE_DRIVERS
bool "Support multiple cpuidle drivers"
depends on CPU_IDLE
default n
help
Allows the cpuidle framework to use different drivers for each CPU.
This is useful if you have a system with different CPU latencies and
states. If unsure say N.

config CPU_IDLE_GOV_LADDER
bool
depends on CPU_IDLE
Expand Down
55 changes: 31 additions & 24 deletions drivers/cpuidle/cpuidle.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ static cpuidle_enter_t cpuidle_enter_ops;
int cpuidle_play_dead(void)
{
struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
struct cpuidle_driver *drv = cpuidle_get_driver();
struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
int i, dead_state = -1;
int power_usage = -1;

Expand Down Expand Up @@ -109,8 +109,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
/* This can be moved to within driver enter routine
* but that results in multiple copies of same code.
*/
dev->states_usage[entered_state].time +=
(unsigned long long)dev->last_residency;
dev->states_usage[entered_state].time += dev->last_residency;
dev->states_usage[entered_state].usage++;
} else {
dev->last_residency = 0;
Expand All @@ -128,7 +127,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
int cpuidle_idle_call(void)
{
struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
struct cpuidle_driver *drv = cpuidle_get_driver();
struct cpuidle_driver *drv;
int next_state, entered_state;

if (off)
Expand All @@ -141,9 +140,15 @@ int cpuidle_idle_call(void)
if (!dev || !dev->enabled)
return -EBUSY;

drv = cpuidle_get_cpu_driver(dev);

/* ask the governor for the next state */
next_state = cpuidle_curr_governor->select(drv, dev);
if (need_resched()) {
dev->last_residency = 0;
/* give the governor an opportunity to reflect on the outcome */
if (cpuidle_curr_governor->reflect)
cpuidle_curr_governor->reflect(dev, next_state);
local_irq_enable();
return 0;
}
Expand Down Expand Up @@ -308,15 +313,19 @@ static void poll_idle_init(struct cpuidle_driver *drv) {}
int cpuidle_enable_device(struct cpuidle_device *dev)
{
int ret, i;
struct cpuidle_driver *drv = cpuidle_get_driver();
struct cpuidle_driver *drv;

if (!dev)
return -EINVAL;

if (dev->enabled)
return 0;

drv = cpuidle_get_cpu_driver(dev);

if (!drv || !cpuidle_curr_governor)
return -EIO;

if (!dev->state_count)
dev->state_count = drv->state_count;

Expand All @@ -331,7 +340,8 @@ int cpuidle_enable_device(struct cpuidle_device *dev)

poll_idle_init(drv);

if ((ret = cpuidle_add_state_sysfs(dev)))
ret = cpuidle_add_device_sysfs(dev);
if (ret)
return ret;

if (cpuidle_curr_governor->enable &&
Expand All @@ -352,7 +362,7 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
return 0;

fail_sysfs:
cpuidle_remove_state_sysfs(dev);
cpuidle_remove_device_sysfs(dev);

return ret;
}
Expand All @@ -368,17 +378,20 @@ EXPORT_SYMBOL_GPL(cpuidle_enable_device);
*/
void cpuidle_disable_device(struct cpuidle_device *dev)
{
struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);

if (!dev || !dev->enabled)
return;
if (!cpuidle_get_driver() || !cpuidle_curr_governor)

if (!drv || !cpuidle_curr_governor)
return;

dev->enabled = 0;

if (cpuidle_curr_governor->disable)
cpuidle_curr_governor->disable(cpuidle_get_driver(), dev);
cpuidle_curr_governor->disable(drv, dev);

cpuidle_remove_state_sysfs(dev);
cpuidle_remove_device_sysfs(dev);
enabled_devices--;
}

Expand All @@ -394,17 +407,14 @@ EXPORT_SYMBOL_GPL(cpuidle_disable_device);
static int __cpuidle_register_device(struct cpuidle_device *dev)
{
int ret;
struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu);
struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);

if (!try_module_get(cpuidle_driver->owner))
if (!try_module_get(drv->owner))
return -EINVAL;

init_completion(&dev->kobj_unregister);

per_cpu(cpuidle_devices, dev->cpu) = dev;
list_add(&dev->device_list, &cpuidle_detected_devices);
ret = cpuidle_add_sysfs(cpu_dev);
ret = cpuidle_add_sysfs(dev);
if (ret)
goto err_sysfs;

Expand All @@ -416,12 +426,11 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)
return 0;

err_coupled:
cpuidle_remove_sysfs(cpu_dev);
wait_for_completion(&dev->kobj_unregister);
cpuidle_remove_sysfs(dev);
err_sysfs:
list_del(&dev->device_list);
per_cpu(cpuidle_devices, dev->cpu) = NULL;
module_put(cpuidle_driver->owner);
module_put(drv->owner);
return ret;
}

Expand Down Expand Up @@ -460,8 +469,7 @@ EXPORT_SYMBOL_GPL(cpuidle_register_device);
*/
void cpuidle_unregister_device(struct cpuidle_device *dev)
{
struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu);
struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);

if (dev->registered == 0)
return;
Expand All @@ -470,16 +478,15 @@ void cpuidle_unregister_device(struct cpuidle_device *dev)

cpuidle_disable_device(dev);

cpuidle_remove_sysfs(cpu_dev);
cpuidle_remove_sysfs(dev);
list_del(&dev->device_list);
wait_for_completion(&dev->kobj_unregister);
per_cpu(cpuidle_devices, dev->cpu) = NULL;

cpuidle_coupled_unregister_device(dev);

cpuidle_resume_and_unlock();

module_put(cpuidle_driver->owner);
module_put(drv->owner);
}

EXPORT_SYMBOL_GPL(cpuidle_unregister_device);
Expand Down
13 changes: 7 additions & 6 deletions drivers/cpuidle/cpuidle.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#ifndef __DRIVER_CPUIDLE_H
#define __DRIVER_CPUIDLE_H

#include <linux/device.h>

/* For internal use only */
extern struct cpuidle_governor *cpuidle_curr_governor;
extern struct list_head cpuidle_governors;
Expand All @@ -25,12 +23,15 @@ extern void cpuidle_uninstall_idle_handler(void);
extern int cpuidle_switch_governor(struct cpuidle_governor *gov);

/* sysfs */

struct device;

extern int cpuidle_add_interface(struct device *dev);
extern void cpuidle_remove_interface(struct device *dev);
extern int cpuidle_add_state_sysfs(struct cpuidle_device *device);
extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device);
extern int cpuidle_add_sysfs(struct device *dev);
extern void cpuidle_remove_sysfs(struct device *dev);
extern int cpuidle_add_device_sysfs(struct cpuidle_device *device);
extern void cpuidle_remove_device_sysfs(struct cpuidle_device *device);
extern int cpuidle_add_sysfs(struct cpuidle_device *dev);
extern void cpuidle_remove_sysfs(struct cpuidle_device *dev);

#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
bool cpuidle_state_is_coupled(struct cpuidle_device *dev,
Expand Down
Loading

0 comments on commit 9ee71f5

Please sign in to comment.