Commit 1fe0135b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pm+acpi-3.11-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull ACPI and power management fixes from Rafael Wysocki:

 - Revert two cpuidle commits added during the 3.8 development cycle
   that turn out to have introduced a significant performance regression
   as requested by Jeremy Eder.

 - The recent patches that made the freezer less heavy-weight introduced
   a regression causing user-space-driven hibernation using the ioctl()
   interface to block indefinitely when the hibernate process executes
   try_to_freeze().  Fix from Colin Cross addresses this by adding a
   process flag to mark the hibernate/suspend process to inform the
   freezer that that process should be ignored.

 - One of the recent cpufreq reverts uncovered a problem in the core
   causing the cpufreq driver module refcount to become negative after a
   system suspend-resume cycle.  Fix from Rafael J Wysocki.

 - The evaluation of the ACPI battery _BIX method has never worked
   correctly, because the commit that added support for it forgot to
   take the "Revision" field in the return package into account.  As a
   result, the reading of battery info doesn't work at all on some
   systems, which is addressed by a fix from Lan Tianyu.

* tag 'pm+acpi-3.11-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  freezer: set PF_SUSPEND_TASK flag on tasks that call freeze_processes
  ACPI / battery: Fix parsing _BIX return value
  cpufreq: Fix cpufreq driver module refcount balance after suspend/resume
  Revert "cpuidle: Quickly notice prediction failure for repeat mode"
  Revert "cpuidle: Quickly notice prediction failure in general case"
parents 64ccccf8 2b44c4db
...@@ -117,6 +117,7 @@ struct acpi_battery { ...@@ -117,6 +117,7 @@ struct acpi_battery {
struct acpi_device *device; struct acpi_device *device;
struct notifier_block pm_nb; struct notifier_block pm_nb;
unsigned long update_time; unsigned long update_time;
int revision;
int rate_now; int rate_now;
int capacity_now; int capacity_now;
int voltage_now; int voltage_now;
...@@ -359,6 +360,7 @@ static struct acpi_offsets info_offsets[] = { ...@@ -359,6 +360,7 @@ static struct acpi_offsets info_offsets[] = {
}; };
static struct acpi_offsets extended_info_offsets[] = { static struct acpi_offsets extended_info_offsets[] = {
{offsetof(struct acpi_battery, revision), 0},
{offsetof(struct acpi_battery, power_unit), 0}, {offsetof(struct acpi_battery, power_unit), 0},
{offsetof(struct acpi_battery, design_capacity), 0}, {offsetof(struct acpi_battery, design_capacity), 0},
{offsetof(struct acpi_battery, full_charge_capacity), 0}, {offsetof(struct acpi_battery, full_charge_capacity), 0},
......
...@@ -1177,14 +1177,11 @@ static int __cpufreq_remove_dev(struct device *dev, ...@@ -1177,14 +1177,11 @@ static int __cpufreq_remove_dev(struct device *dev,
__func__, cpu_dev->id, cpu); __func__, cpu_dev->id, cpu);
} }
if ((cpus == 1) && (cpufreq_driver->target))
__cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
cpufreq_cpu_put(data);
/* If cpu is last user of policy, free policy */ /* If cpu is last user of policy, free policy */
if (cpus == 1) { if (cpus == 1) {
if (cpufreq_driver->target)
__cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
lock_policy_rwsem_read(cpu); lock_policy_rwsem_read(cpu);
kobj = &data->kobj; kobj = &data->kobj;
cmp = &data->kobj_unregister; cmp = &data->kobj_unregister;
...@@ -1205,10 +1202,14 @@ static int __cpufreq_remove_dev(struct device *dev, ...@@ -1205,10 +1202,14 @@ static int __cpufreq_remove_dev(struct device *dev,
free_cpumask_var(data->related_cpus); free_cpumask_var(data->related_cpus);
free_cpumask_var(data->cpus); free_cpumask_var(data->cpus);
kfree(data); kfree(data);
} else if (cpufreq_driver->target) { } else {
pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
cpufreq_cpu_put(data);
if (cpufreq_driver->target) {
__cpufreq_governor(data, CPUFREQ_GOV_START); __cpufreq_governor(data, CPUFREQ_GOV_START);
__cpufreq_governor(data, CPUFREQ_GOV_LIMITS); __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
} }
}
per_cpu(cpufreq_policy_cpu, cpu) = -1; per_cpu(cpufreq_policy_cpu, cpu) = -1;
return 0; return 0;
......
...@@ -28,13 +28,6 @@ ...@@ -28,13 +28,6 @@
#define MAX_INTERESTING 50000 #define MAX_INTERESTING 50000
#define STDDEV_THRESH 400 #define STDDEV_THRESH 400
/* 60 * 60 > STDDEV_THRESH * INTERVALS = 400 * 8 */
#define MAX_DEVIATION 60
static DEFINE_PER_CPU(struct hrtimer, menu_hrtimer);
static DEFINE_PER_CPU(int, hrtimer_status);
/* menu hrtimer mode */
enum {MENU_HRTIMER_STOP, MENU_HRTIMER_REPEAT, MENU_HRTIMER_GENERAL};
/* /*
* Concepts and ideas behind the menu governor * Concepts and ideas behind the menu governor
...@@ -116,13 +109,6 @@ enum {MENU_HRTIMER_STOP, MENU_HRTIMER_REPEAT, MENU_HRTIMER_GENERAL}; ...@@ -116,13 +109,6 @@ enum {MENU_HRTIMER_STOP, MENU_HRTIMER_REPEAT, MENU_HRTIMER_GENERAL};
* *
*/ */
/*
* The C-state residency is so long that is is worthwhile to exit
* from the shallow C-state and re-enter into a deeper C-state.
*/
static unsigned int perfect_cstate_ms __read_mostly = 30;
module_param(perfect_cstate_ms, uint, 0000);
struct menu_device { struct menu_device {
int last_state_idx; int last_state_idx;
int needs_update; int needs_update;
...@@ -205,52 +191,17 @@ static u64 div_round64(u64 dividend, u32 divisor) ...@@ -205,52 +191,17 @@ static u64 div_round64(u64 dividend, u32 divisor)
return div_u64(dividend + (divisor / 2), divisor); return div_u64(dividend + (divisor / 2), divisor);
} }
/* Cancel the hrtimer if it is not triggered yet */
void menu_hrtimer_cancel(void)
{
int cpu = smp_processor_id();
struct hrtimer *hrtmr = &per_cpu(menu_hrtimer, cpu);
/* The timer is still not time out*/
if (per_cpu(hrtimer_status, cpu)) {
hrtimer_cancel(hrtmr);
per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_STOP;
}
}
EXPORT_SYMBOL_GPL(menu_hrtimer_cancel);
/* Call back for hrtimer is triggered */
static enum hrtimer_restart menu_hrtimer_notify(struct hrtimer *hrtimer)
{
int cpu = smp_processor_id();
struct menu_device *data = &per_cpu(menu_devices, cpu);
/* In general case, the expected residency is much larger than
* deepest C-state target residency, but prediction logic still
* predicts a small predicted residency, so the prediction
* history is totally broken if the timer is triggered.
* So reset the correction factor.
*/
if (per_cpu(hrtimer_status, cpu) == MENU_HRTIMER_GENERAL)
data->correction_factor[data->bucket] = RESOLUTION * DECAY;
per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_STOP;
return HRTIMER_NORESTART;
}
/* /*
* Try detecting repeating patterns by keeping track of the last 8 * Try detecting repeating patterns by keeping track of the last 8
* intervals, and checking if the standard deviation of that set * intervals, and checking if the standard deviation of that set
* of points is below a threshold. If it is... then use the * of points is below a threshold. If it is... then use the
* average of these 8 points as the estimated value. * average of these 8 points as the estimated value.
*/ */
static u32 get_typical_interval(struct menu_device *data) static void get_typical_interval(struct menu_device *data)
{ {
int i = 0, divisor = 0; int i = 0, divisor = 0;
uint64_t max = 0, avg = 0, stddev = 0; uint64_t max = 0, avg = 0, stddev = 0;
int64_t thresh = LLONG_MAX; /* Discard outliers above this value. */ int64_t thresh = LLONG_MAX; /* Discard outliers above this value. */
unsigned int ret = 0;
again: again:
...@@ -291,16 +242,13 @@ static u32 get_typical_interval(struct menu_device *data) ...@@ -291,16 +242,13 @@ static u32 get_typical_interval(struct menu_device *data)
if (((avg > stddev * 6) && (divisor * 4 >= INTERVALS * 3)) if (((avg > stddev * 6) && (divisor * 4 >= INTERVALS * 3))
|| stddev <= 20) { || stddev <= 20) {
data->predicted_us = avg; data->predicted_us = avg;
ret = 1; return;
return ret;
} else if ((divisor * 4) > INTERVALS * 3) { } else if ((divisor * 4) > INTERVALS * 3) {
/* Exclude the max interval */ /* Exclude the max interval */
thresh = max - 1; thresh = max - 1;
goto again; goto again;
} }
return ret;
} }
/** /**
...@@ -315,9 +263,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) ...@@ -315,9 +263,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
int i; int i;
int multiplier; int multiplier;
struct timespec t; struct timespec t;
int repeat = 0, low_predicted = 0;
int cpu = smp_processor_id();
struct hrtimer *hrtmr = &per_cpu(menu_hrtimer, cpu);
if (data->needs_update) { if (data->needs_update) {
menu_update(drv, dev); menu_update(drv, dev);
...@@ -352,7 +297,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) ...@@ -352,7 +297,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket], data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket],
RESOLUTION * DECAY); RESOLUTION * DECAY);
repeat = get_typical_interval(data); get_typical_interval(data);
/* /*
* We want to default to C1 (hlt), not to busy polling * We want to default to C1 (hlt), not to busy polling
...@@ -373,10 +318,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) ...@@ -373,10 +318,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
if (s->disabled || su->disable) if (s->disabled || su->disable)
continue; continue;
if (s->target_residency > data->predicted_us) { if (s->target_residency > data->predicted_us)
low_predicted = 1;
continue; continue;
}
if (s->exit_latency > latency_req) if (s->exit_latency > latency_req)
continue; continue;
if (s->exit_latency * multiplier > data->predicted_us) if (s->exit_latency * multiplier > data->predicted_us)
...@@ -386,44 +329,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) ...@@ -386,44 +329,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
data->exit_us = s->exit_latency; data->exit_us = s->exit_latency;
} }
/* not deepest C-state chosen for low predicted residency */
if (low_predicted) {
unsigned int timer_us = 0;
unsigned int perfect_us = 0;
/*
* Set a timer to detect whether this sleep is much
* longer than repeat mode predicted. If the timer
* triggers, the code will evaluate whether to put
* the CPU into a deeper C-state.
* The timer is cancelled on CPU wakeup.
*/
timer_us = 2 * (data->predicted_us + MAX_DEVIATION);
perfect_us = perfect_cstate_ms * 1000;
if (repeat && (4 * timer_us < data->expected_us)) {
RCU_NONIDLE(hrtimer_start(hrtmr,
ns_to_ktime(1000 * timer_us),
HRTIMER_MODE_REL_PINNED));
/* In repeat case, menu hrtimer is started */
per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_REPEAT;
} else if (perfect_us < data->expected_us) {
/*
* The next timer is long. This could be because
* we did not make a useful prediction.
* In that case, it makes sense to re-enter
* into a deeper C-state after some time.
*/
RCU_NONIDLE(hrtimer_start(hrtmr,
ns_to_ktime(1000 * timer_us),
HRTIMER_MODE_REL_PINNED));
/* In general case, menu hrtimer is started */
per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_GENERAL;
}
}
return data->last_state_idx; return data->last_state_idx;
} }
...@@ -514,9 +419,6 @@ static int menu_enable_device(struct cpuidle_driver *drv, ...@@ -514,9 +419,6 @@ static int menu_enable_device(struct cpuidle_driver *drv,
struct cpuidle_device *dev) struct cpuidle_device *dev)
{ {
struct menu_device *data = &per_cpu(menu_devices, dev->cpu); struct menu_device *data = &per_cpu(menu_devices, dev->cpu);
struct hrtimer *t = &per_cpu(menu_hrtimer, dev->cpu);
hrtimer_init(t, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
t->function = menu_hrtimer_notify;
memset(data, 0, sizeof(struct menu_device)); memset(data, 0, sizeof(struct menu_device));
......
...@@ -1628,6 +1628,7 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, ...@@ -1628,6 +1628,7 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut,
#define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */ #define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */
#define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */ #define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezable */ #define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezable */
#define PF_SUSPEND_TASK 0x80000000 /* this thread called freeze_processes and should not be frozen */
/* /*
* Only the _current_ task can read/write to tsk->flags, but other * Only the _current_ task can read/write to tsk->flags, but other
......
...@@ -174,10 +174,4 @@ static inline void tick_nohz_task_switch(struct task_struct *tsk) { } ...@@ -174,10 +174,4 @@ static inline void tick_nohz_task_switch(struct task_struct *tsk) { }
#endif #endif
# ifdef CONFIG_CPU_IDLE_GOV_MENU
extern void menu_hrtimer_cancel(void);
# else
static inline void menu_hrtimer_cancel(void) {}
# endif /* CONFIG_CPU_IDLE_GOV_MENU */
#endif #endif
...@@ -33,7 +33,7 @@ static DEFINE_SPINLOCK(freezer_lock); ...@@ -33,7 +33,7 @@ static DEFINE_SPINLOCK(freezer_lock);
*/ */
bool freezing_slow_path(struct task_struct *p) bool freezing_slow_path(struct task_struct *p)
{ {
if (p->flags & PF_NOFREEZE) if (p->flags & (PF_NOFREEZE | PF_SUSPEND_TASK))
return false; return false;
if (pm_nosig_freezing || cgroup_freezing(p)) if (pm_nosig_freezing || cgroup_freezing(p))
......
...@@ -109,6 +109,8 @@ static int try_to_freeze_tasks(bool user_only) ...@@ -109,6 +109,8 @@ static int try_to_freeze_tasks(bool user_only)
/** /**
* freeze_processes - Signal user space processes to enter the refrigerator. * freeze_processes - Signal user space processes to enter the refrigerator.
* The current thread will not be frozen. The same process that calls
* freeze_processes must later call thaw_processes.
* *
* On success, returns 0. On failure, -errno and system is fully thawed. * On success, returns 0. On failure, -errno and system is fully thawed.
*/ */
...@@ -120,6 +122,9 @@ int freeze_processes(void) ...@@ -120,6 +122,9 @@ int freeze_processes(void)
if (error) if (error)
return error; return error;
/* Make sure this task doesn't get frozen */
current->flags |= PF_SUSPEND_TASK;
if (!pm_freezing) if (!pm_freezing)
atomic_inc(&system_freezing_cnt); atomic_inc(&system_freezing_cnt);
...@@ -168,6 +173,7 @@ int freeze_kernel_threads(void) ...@@ -168,6 +173,7 @@ int freeze_kernel_threads(void)
void thaw_processes(void) void thaw_processes(void)
{ {
struct task_struct *g, *p; struct task_struct *g, *p;
struct task_struct *curr = current;
if (pm_freezing) if (pm_freezing)
atomic_dec(&system_freezing_cnt); atomic_dec(&system_freezing_cnt);
...@@ -182,10 +188,15 @@ void thaw_processes(void) ...@@ -182,10 +188,15 @@ void thaw_processes(void)
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
do_each_thread(g, p) { do_each_thread(g, p) {
/* No other threads should have PF_SUSPEND_TASK set */
WARN_ON((p != curr) && (p->flags & PF_SUSPEND_TASK));
__thaw_task(p); __thaw_task(p);
} while_each_thread(g, p); } while_each_thread(g, p);
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
WARN_ON(!(curr->flags & PF_SUSPEND_TASK));
curr->flags &= ~PF_SUSPEND_TASK;
usermodehelper_enable(); usermodehelper_enable();
schedule(); schedule();
......
...@@ -827,13 +827,10 @@ void tick_nohz_irq_exit(void) ...@@ -827,13 +827,10 @@ void tick_nohz_irq_exit(void)
{ {
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
if (ts->inidle) { if (ts->inidle)
/* Cancel the timer because CPU already waken up from the C-states*/
menu_hrtimer_cancel();
__tick_nohz_idle_enter(ts); __tick_nohz_idle_enter(ts);
} else { else
tick_nohz_full_stop_tick(ts); tick_nohz_full_stop_tick(ts);
}
} }
/** /**
...@@ -931,8 +928,6 @@ void tick_nohz_idle_exit(void) ...@@ -931,8 +928,6 @@ void tick_nohz_idle_exit(void)
ts->inidle = 0; ts->inidle = 0;
/* Cancel the timer because CPU already waken up from the C-states*/
menu_hrtimer_cancel();
if (ts->idle_active || ts->tick_stopped) if (ts->idle_active || ts->tick_stopped)
now = ktime_get(); now = ktime_get();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment