Merge tag 'pm-for-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki: - Implementation of opportunistic suspend (autosleep) and user space interface for manipulating wakeup sources. - Hibernate updates from Bojan Smojver and Minho Ban. - Updates of the runtime PM core and generic PM domains framework related to PM QoS. - Assorted fixes. * tag 'pm-for-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (25 commits) epoll: Fix user space breakage related to EPOLLWAKEUP PM / Domains: Make it possible to add devices to inactive domains PM / Hibernate: Use get_gendisk to verify partition if resume_file is integer format PM / Domains: Fix computation of maximum domain off time PM / Domains: Fix link checking when add subdomain PM / Sleep: User space wakeup sources garbage collector Kconfig option PM / Sleep: Make the limit of user space wakeup sources configurable PM / Documentation: suspend-and-cpuhotplug.txt: Fix typo PM / Domains: Cache device stop and domain power off governor results, v3 PM / Domains: Make device removal more straightforward PM / Sleep: Fix a mistake in a conditional in autosleep_store() epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready PM / QoS: Create device constraints objects on notifier registration PM / Runtime: Remove device fields related to suspend time, v2 PM / Domains: Rework default domain power off governor function, v2 PM / Domains: Rework default device stop governor function, v2 PM / Sleep: Add user space interface for manipulating wakeup sources, v3 PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources PM / Sleep: Implement opportunistic sleep, v2 PM / Sleep: Add wakeup_source_activate and wakeup_source_deactivate tracepoints ...
This commit is contained in:
@@ -103,6 +103,33 @@ config PM_SLEEP_SMP
|
||||
select HOTPLUG
|
||||
select HOTPLUG_CPU
|
||||
|
||||
config PM_AUTOSLEEP
|
||||
bool "Opportunistic sleep"
|
||||
depends on PM_SLEEP
|
||||
default n
|
||||
---help---
|
||||
Allow the kernel to trigger a system transition into a global sleep
|
||||
state automatically whenever there are no active wakeup sources.
|
||||
|
||||
config PM_WAKELOCKS
|
||||
bool "User space wakeup sources interface"
|
||||
depends on PM_SLEEP
|
||||
default n
|
||||
---help---
|
||||
Allow user space to create, activate and deactivate wakeup source
|
||||
objects with the help of a sysfs-based interface.
|
||||
|
||||
config PM_WAKELOCKS_LIMIT
|
||||
int "Maximum number of user space wakeup sources (0 = no limit)"
|
||||
range 0 100000
|
||||
default 100
|
||||
depends on PM_WAKELOCKS
|
||||
|
||||
config PM_WAKELOCKS_GC
|
||||
bool "Garbage collector for user space wakeup sources"
|
||||
depends on PM_WAKELOCKS
|
||||
default y
|
||||
|
||||
config PM_RUNTIME
|
||||
bool "Run-time PM core functionality"
|
||||
depends on !IA64_HP_SIM
|
||||
|
||||
@@ -9,5 +9,7 @@ obj-$(CONFIG_SUSPEND) += suspend.o
|
||||
obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o
|
||||
obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \
|
||||
block_io.o
|
||||
obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o
|
||||
obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o
|
||||
|
||||
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* kernel/power/autosleep.c
|
||||
*
|
||||
* Opportunistic sleep support.
|
||||
*
|
||||
* Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
static suspend_state_t autosleep_state;
|
||||
static struct workqueue_struct *autosleep_wq;
|
||||
/*
|
||||
* Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
|
||||
* is active, otherwise a deadlock with try_to_suspend() is possible.
|
||||
* Alternatively mutex_lock_interruptible() can be used. This will then fail
|
||||
* if an auto_sleep cycle tries to freeze processes.
|
||||
*/
|
||||
static DEFINE_MUTEX(autosleep_lock);
|
||||
static struct wakeup_source *autosleep_ws;
|
||||
|
||||
static void try_to_suspend(struct work_struct *work)
|
||||
{
|
||||
unsigned int initial_count, final_count;
|
||||
|
||||
if (!pm_get_wakeup_count(&initial_count, true))
|
||||
goto out;
|
||||
|
||||
mutex_lock(&autosleep_lock);
|
||||
|
||||
if (!pm_save_wakeup_count(initial_count)) {
|
||||
mutex_unlock(&autosleep_lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (autosleep_state == PM_SUSPEND_ON) {
|
||||
mutex_unlock(&autosleep_lock);
|
||||
return;
|
||||
}
|
||||
if (autosleep_state >= PM_SUSPEND_MAX)
|
||||
hibernate();
|
||||
else
|
||||
pm_suspend(autosleep_state);
|
||||
|
||||
mutex_unlock(&autosleep_lock);
|
||||
|
||||
if (!pm_get_wakeup_count(&final_count, false))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If the wakeup occured for an unknown reason, wait to prevent the
|
||||
* system from trying to suspend and waking up in a tight loop.
|
||||
*/
|
||||
if (final_count == initial_count)
|
||||
schedule_timeout_uninterruptible(HZ / 2);
|
||||
|
||||
out:
|
||||
queue_up_suspend_work();
|
||||
}
|
||||
|
||||
static DECLARE_WORK(suspend_work, try_to_suspend);
|
||||
|
||||
void queue_up_suspend_work(void)
|
||||
{
|
||||
if (!work_pending(&suspend_work) && autosleep_state > PM_SUSPEND_ON)
|
||||
queue_work(autosleep_wq, &suspend_work);
|
||||
}
|
||||
|
||||
suspend_state_t pm_autosleep_state(void)
|
||||
{
|
||||
return autosleep_state;
|
||||
}
|
||||
|
||||
int pm_autosleep_lock(void)
|
||||
{
|
||||
return mutex_lock_interruptible(&autosleep_lock);
|
||||
}
|
||||
|
||||
void pm_autosleep_unlock(void)
|
||||
{
|
||||
mutex_unlock(&autosleep_lock);
|
||||
}
|
||||
|
||||
int pm_autosleep_set_state(suspend_state_t state)
|
||||
{
|
||||
|
||||
#ifndef CONFIG_HIBERNATION
|
||||
if (state >= PM_SUSPEND_MAX)
|
||||
return -EINVAL;
|
||||
#endif
|
||||
|
||||
__pm_stay_awake(autosleep_ws);
|
||||
|
||||
mutex_lock(&autosleep_lock);
|
||||
|
||||
autosleep_state = state;
|
||||
|
||||
__pm_relax(autosleep_ws);
|
||||
|
||||
if (state > PM_SUSPEND_ON) {
|
||||
pm_wakep_autosleep_enabled(true);
|
||||
queue_up_suspend_work();
|
||||
} else {
|
||||
pm_wakep_autosleep_enabled(false);
|
||||
}
|
||||
|
||||
mutex_unlock(&autosleep_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init pm_autosleep_init(void)
|
||||
{
|
||||
autosleep_ws = wakeup_source_register("autosleep");
|
||||
if (!autosleep_ws)
|
||||
return -ENOMEM;
|
||||
|
||||
autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
|
||||
if (autosleep_wq)
|
||||
return 0;
|
||||
|
||||
wakeup_source_unregister(autosleep_ws);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <scsi/scsi_scan.h>
|
||||
|
||||
#include "power.h"
|
||||
@@ -722,6 +724,17 @@ static int software_resume(void)
|
||||
|
||||
/* Check if the device is there */
|
||||
swsusp_resume_device = name_to_dev_t(resume_file);
|
||||
|
||||
/*
|
||||
* name_to_dev_t is ineffective to verify parition if resume_file is in
|
||||
* integer format. (e.g. major:minor)
|
||||
*/
|
||||
if (isdigit(resume_file[0]) && resume_wait) {
|
||||
int partno;
|
||||
while (!get_gendisk(swsusp_resume_device, &partno))
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
if (!swsusp_resume_device) {
|
||||
/*
|
||||
* Some device discovery might still be in progress; we need
|
||||
|
||||
+141
-19
@@ -269,8 +269,7 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
return (s - buf);
|
||||
}
|
||||
|
||||
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
static suspend_state_t decode_state(const char *buf, size_t n)
|
||||
{
|
||||
#ifdef CONFIG_SUSPEND
|
||||
suspend_state_t state = PM_SUSPEND_STANDBY;
|
||||
@@ -278,27 +277,48 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
#endif
|
||||
char *p;
|
||||
int len;
|
||||
int error = -EINVAL;
|
||||
|
||||
p = memchr(buf, '\n', n);
|
||||
len = p ? p - buf : n;
|
||||
|
||||
/* First, check if we are requested to hibernate */
|
||||
if (len == 4 && !strncmp(buf, "disk", len)) {
|
||||
error = hibernate();
|
||||
goto Exit;
|
||||
}
|
||||
/* Check hibernation first. */
|
||||
if (len == 4 && !strncmp(buf, "disk", len))
|
||||
return PM_SUSPEND_MAX;
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
|
||||
if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
|
||||
error = pm_suspend(state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++)
|
||||
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
|
||||
return state;
|
||||
#endif
|
||||
|
||||
Exit:
|
||||
return PM_SUSPEND_ON;
|
||||
}
|
||||
|
||||
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
suspend_state_t state;
|
||||
int error;
|
||||
|
||||
error = pm_autosleep_lock();
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (pm_autosleep_state() > PM_SUSPEND_ON) {
|
||||
error = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
state = decode_state(buf, n);
|
||||
if (state < PM_SUSPEND_MAX)
|
||||
error = pm_suspend(state);
|
||||
else if (state == PM_SUSPEND_MAX)
|
||||
error = hibernate();
|
||||
else
|
||||
error = -EINVAL;
|
||||
|
||||
out:
|
||||
pm_autosleep_unlock();
|
||||
return error ? error : n;
|
||||
}
|
||||
|
||||
@@ -339,7 +359,8 @@ static ssize_t wakeup_count_show(struct kobject *kobj,
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
return pm_get_wakeup_count(&val) ? sprintf(buf, "%u\n", val) : -EINTR;
|
||||
return pm_get_wakeup_count(&val, true) ?
|
||||
sprintf(buf, "%u\n", val) : -EINTR;
|
||||
}
|
||||
|
||||
static ssize_t wakeup_count_store(struct kobject *kobj,
|
||||
@@ -347,15 +368,106 @@ static ssize_t wakeup_count_store(struct kobject *kobj,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
unsigned int val;
|
||||
int error;
|
||||
|
||||
error = pm_autosleep_lock();
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (pm_autosleep_state() > PM_SUSPEND_ON) {
|
||||
error = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = -EINVAL;
|
||||
if (sscanf(buf, "%u", &val) == 1) {
|
||||
if (pm_save_wakeup_count(val))
|
||||
return n;
|
||||
error = n;
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
out:
|
||||
pm_autosleep_unlock();
|
||||
return error;
|
||||
}
|
||||
|
||||
power_attr(wakeup_count);
|
||||
|
||||
#ifdef CONFIG_PM_AUTOSLEEP
|
||||
static ssize_t autosleep_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
suspend_state_t state = pm_autosleep_state();
|
||||
|
||||
if (state == PM_SUSPEND_ON)
|
||||
return sprintf(buf, "off\n");
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
if (state < PM_SUSPEND_MAX)
|
||||
return sprintf(buf, "%s\n", valid_state(state) ?
|
||||
pm_states[state] : "error");
|
||||
#endif
|
||||
#ifdef CONFIG_HIBERNATION
|
||||
return sprintf(buf, "disk\n");
|
||||
#else
|
||||
return sprintf(buf, "error");
|
||||
#endif
|
||||
}
|
||||
|
||||
static ssize_t autosleep_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
suspend_state_t state = decode_state(buf, n);
|
||||
int error;
|
||||
|
||||
if (state == PM_SUSPEND_ON
|
||||
&& strcmp(buf, "off") && strcmp(buf, "off\n"))
|
||||
return -EINVAL;
|
||||
|
||||
error = pm_autosleep_set_state(state);
|
||||
return error ? error : n;
|
||||
}
|
||||
|
||||
power_attr(autosleep);
|
||||
#endif /* CONFIG_PM_AUTOSLEEP */
|
||||
|
||||
#ifdef CONFIG_PM_WAKELOCKS
|
||||
static ssize_t wake_lock_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return pm_show_wakelocks(buf, true);
|
||||
}
|
||||
|
||||
static ssize_t wake_lock_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
int error = pm_wake_lock(buf);
|
||||
return error ? error : n;
|
||||
}
|
||||
|
||||
power_attr(wake_lock);
|
||||
|
||||
static ssize_t wake_unlock_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return pm_show_wakelocks(buf, false);
|
||||
}
|
||||
|
||||
static ssize_t wake_unlock_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
int error = pm_wake_unlock(buf);
|
||||
return error ? error : n;
|
||||
}
|
||||
|
||||
power_attr(wake_unlock);
|
||||
|
||||
#endif /* CONFIG_PM_WAKELOCKS */
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
@@ -409,6 +521,13 @@ static struct attribute * g[] = {
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
&pm_async_attr.attr,
|
||||
&wakeup_count_attr.attr,
|
||||
#ifdef CONFIG_PM_AUTOSLEEP
|
||||
&autosleep_attr.attr,
|
||||
#endif
|
||||
#ifdef CONFIG_PM_WAKELOCKS
|
||||
&wake_lock_attr.attr,
|
||||
&wake_unlock_attr.attr,
|
||||
#endif
|
||||
#ifdef CONFIG_PM_DEBUG
|
||||
&pm_test_attr.attr,
|
||||
#endif
|
||||
@@ -444,7 +563,10 @@ static int __init pm_init(void)
|
||||
power_kobj = kobject_create_and_add("power", NULL);
|
||||
if (!power_kobj)
|
||||
return -ENOMEM;
|
||||
return sysfs_create_group(power_kobj, &attr_group);
|
||||
error = sysfs_create_group(power_kobj, &attr_group);
|
||||
if (error)
|
||||
return error;
|
||||
return pm_autosleep_init();
|
||||
}
|
||||
|
||||
core_initcall(pm_init);
|
||||
|
||||
@@ -264,3 +264,30 @@ static inline void suspend_thaw_processes(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_AUTOSLEEP
|
||||
|
||||
/* kernel/power/autosleep.c */
|
||||
extern int pm_autosleep_init(void);
|
||||
extern int pm_autosleep_lock(void);
|
||||
extern void pm_autosleep_unlock(void);
|
||||
extern suspend_state_t pm_autosleep_state(void);
|
||||
extern int pm_autosleep_set_state(suspend_state_t state);
|
||||
|
||||
#else /* !CONFIG_PM_AUTOSLEEP */
|
||||
|
||||
static inline int pm_autosleep_init(void) { return 0; }
|
||||
static inline int pm_autosleep_lock(void) { return 0; }
|
||||
static inline void pm_autosleep_unlock(void) {}
|
||||
static inline suspend_state_t pm_autosleep_state(void) { return PM_SUSPEND_ON; }
|
||||
|
||||
#endif /* !CONFIG_PM_AUTOSLEEP */
|
||||
|
||||
#ifdef CONFIG_PM_WAKELOCKS
|
||||
|
||||
/* kernel/power/wakelock.c */
|
||||
extern ssize_t pm_show_wakelocks(char *buf, bool show_active);
|
||||
extern int pm_wake_lock(const char *buf);
|
||||
extern int pm_wake_unlock(const char *buf);
|
||||
|
||||
#endif /* !CONFIG_PM_WAKELOCKS */
|
||||
|
||||
+39
-23
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (C) 1998,2001-2005 Pavel Machek <pavel@ucw.cz>
|
||||
* Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
|
||||
* Copyright (C) 2010 Bojan Smojver <bojan@rexursive.com>
|
||||
* Copyright (C) 2010-2012 Bojan Smojver <bojan@rexursive.com>
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*
|
||||
@@ -282,14 +282,17 @@ static int write_page(void *buf, sector_t offset, struct bio **bio_chain)
|
||||
return -ENOSPC;
|
||||
|
||||
if (bio_chain) {
|
||||
src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
|
||||
src = (void *)__get_free_page(__GFP_WAIT | __GFP_NOWARN |
|
||||
__GFP_NORETRY);
|
||||
if (src) {
|
||||
copy_page(src, buf);
|
||||
} else {
|
||||
ret = hib_wait_on_bio_chain(bio_chain); /* Free pages */
|
||||
if (ret)
|
||||
return ret;
|
||||
src = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
|
||||
src = (void *)__get_free_page(__GFP_WAIT |
|
||||
__GFP_NOWARN |
|
||||
__GFP_NORETRY);
|
||||
if (src) {
|
||||
copy_page(src, buf);
|
||||
} else {
|
||||
@@ -367,12 +370,17 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf,
|
||||
clear_page(handle->cur);
|
||||
handle->cur_swap = offset;
|
||||
handle->k = 0;
|
||||
}
|
||||
if (bio_chain && low_free_pages() <= handle->reqd_free_pages) {
|
||||
error = hib_wait_on_bio_chain(bio_chain);
|
||||
if (error)
|
||||
goto out;
|
||||
handle->reqd_free_pages = reqd_free_pages();
|
||||
|
||||
if (bio_chain && low_free_pages() <= handle->reqd_free_pages) {
|
||||
error = hib_wait_on_bio_chain(bio_chain);
|
||||
if (error)
|
||||
goto out;
|
||||
/*
|
||||
* Recalculate the number of required free pages, to
|
||||
* make sure we never take more than half.
|
||||
*/
|
||||
handle->reqd_free_pages = reqd_free_pages();
|
||||
}
|
||||
}
|
||||
out:
|
||||
return error;
|
||||
@@ -419,8 +427,9 @@ static int swap_writer_finish(struct swap_map_handle *handle,
|
||||
/* Maximum number of threads for compression/decompression. */
|
||||
#define LZO_THREADS 3
|
||||
|
||||
/* Maximum number of pages for read buffering. */
|
||||
#define LZO_READ_PAGES (MAP_PAGE_ENTRIES * 8)
|
||||
/* Minimum/maximum number of pages for read buffering. */
|
||||
#define LZO_MIN_RD_PAGES 1024
|
||||
#define LZO_MAX_RD_PAGES 8192
|
||||
|
||||
|
||||
/**
|
||||
@@ -630,12 +639,6 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust number of free pages after all allocations have been done.
|
||||
* We don't want to run out of pages when writing.
|
||||
*/
|
||||
handle->reqd_free_pages = reqd_free_pages();
|
||||
|
||||
/*
|
||||
* Start the CRC32 thread.
|
||||
*/
|
||||
@@ -657,6 +660,12 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
goto out_clean;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the number of required free pages after all allocations have
|
||||
* been done. We don't want to run out of pages when writing.
|
||||
*/
|
||||
handle->reqd_free_pages = reqd_free_pages();
|
||||
|
||||
printk(KERN_INFO
|
||||
"PM: Using %u thread(s) for compression.\n"
|
||||
"PM: Compressing and saving image data (%u pages) ... ",
|
||||
@@ -1067,7 +1076,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
unsigned i, thr, run_threads, nr_threads;
|
||||
unsigned ring = 0, pg = 0, ring_size = 0,
|
||||
have = 0, want, need, asked = 0;
|
||||
unsigned long read_pages;
|
||||
unsigned long read_pages = 0;
|
||||
unsigned char **page = NULL;
|
||||
struct dec_data *data = NULL;
|
||||
struct crc_data *crc = NULL;
|
||||
@@ -1079,7 +1088,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
nr_threads = num_online_cpus() - 1;
|
||||
nr_threads = clamp_val(nr_threads, 1, LZO_THREADS);
|
||||
|
||||
page = vmalloc(sizeof(*page) * LZO_READ_PAGES);
|
||||
page = vmalloc(sizeof(*page) * LZO_MAX_RD_PAGES);
|
||||
if (!page) {
|
||||
printk(KERN_ERR "PM: Failed to allocate LZO page\n");
|
||||
ret = -ENOMEM;
|
||||
@@ -1144,15 +1153,22 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust number of pages for read buffering, in case we are short.
|
||||
* Set the number of pages for read buffering.
|
||||
* This is complete guesswork, because we'll only know the real
|
||||
* picture once prepare_image() is called, which is much later on
|
||||
* during the image load phase. We'll assume the worst case and
|
||||
* say that none of the image pages are from high memory.
|
||||
*/
|
||||
read_pages = (nr_free_pages() - snapshot_get_image_size()) >> 1;
|
||||
read_pages = clamp_val(read_pages, LZO_CMP_PAGES, LZO_READ_PAGES);
|
||||
if (low_free_pages() > snapshot_get_image_size())
|
||||
read_pages = (low_free_pages() - snapshot_get_image_size()) / 2;
|
||||
read_pages = clamp_val(read_pages, LZO_MIN_RD_PAGES, LZO_MAX_RD_PAGES);
|
||||
|
||||
for (i = 0; i < read_pages; i++) {
|
||||
page[i] = (void *)__get_free_page(i < LZO_CMP_PAGES ?
|
||||
__GFP_WAIT | __GFP_HIGH :
|
||||
__GFP_WAIT);
|
||||
__GFP_WAIT | __GFP_NOWARN |
|
||||
__GFP_NORETRY);
|
||||
|
||||
if (!page[i]) {
|
||||
if (i < LZO_CMP_PAGES) {
|
||||
ring_size = i;
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* kernel/power/wakelock.c
|
||||
*
|
||||
* User space wakeup sources support.
|
||||
*
|
||||
* Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
|
||||
*
|
||||
* This code is based on the analogous interface allowing user space to
|
||||
* manipulate wakelocks on Android.
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static DEFINE_MUTEX(wakelocks_lock);
|
||||
|
||||
struct wakelock {
|
||||
char *name;
|
||||
struct rb_node node;
|
||||
struct wakeup_source ws;
|
||||
#ifdef CONFIG_PM_WAKELOCKS_GC
|
||||
struct list_head lru;
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct rb_root wakelocks_tree = RB_ROOT;
|
||||
|
||||
ssize_t pm_show_wakelocks(char *buf, bool show_active)
|
||||
{
|
||||
struct rb_node *node;
|
||||
struct wakelock *wl;
|
||||
char *str = buf;
|
||||
char *end = buf + PAGE_SIZE;
|
||||
|
||||
mutex_lock(&wakelocks_lock);
|
||||
|
||||
for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
|
||||
wl = rb_entry(node, struct wakelock, node);
|
||||
if (wl->ws.active == show_active)
|
||||
str += scnprintf(str, end - str, "%s ", wl->name);
|
||||
}
|
||||
if (str > buf)
|
||||
str--;
|
||||
|
||||
str += scnprintf(str, end - str, "\n");
|
||||
|
||||
mutex_unlock(&wakelocks_lock);
|
||||
return (str - buf);
|
||||
}
|
||||
|
||||
#if CONFIG_PM_WAKELOCKS_LIMIT > 0
|
||||
static unsigned int number_of_wakelocks;
|
||||
|
||||
static inline bool wakelocks_limit_exceeded(void)
|
||||
{
|
||||
return number_of_wakelocks > CONFIG_PM_WAKELOCKS_LIMIT;
|
||||
}
|
||||
|
||||
static inline void increment_wakelocks_number(void)
|
||||
{
|
||||
number_of_wakelocks++;
|
||||
}
|
||||
|
||||
static inline void decrement_wakelocks_number(void)
|
||||
{
|
||||
number_of_wakelocks--;
|
||||
}
|
||||
#else /* CONFIG_PM_WAKELOCKS_LIMIT = 0 */
|
||||
static inline bool wakelocks_limit_exceeded(void) { return false; }
|
||||
static inline void increment_wakelocks_number(void) {}
|
||||
static inline void decrement_wakelocks_number(void) {}
|
||||
#endif /* CONFIG_PM_WAKELOCKS_LIMIT */
|
||||
|
||||
#ifdef CONFIG_PM_WAKELOCKS_GC
|
||||
#define WL_GC_COUNT_MAX 100
|
||||
#define WL_GC_TIME_SEC 300
|
||||
|
||||
static LIST_HEAD(wakelocks_lru_list);
|
||||
static unsigned int wakelocks_gc_count;
|
||||
|
||||
static inline void wakelocks_lru_add(struct wakelock *wl)
|
||||
{
|
||||
list_add(&wl->lru, &wakelocks_lru_list);
|
||||
}
|
||||
|
||||
static inline void wakelocks_lru_most_recent(struct wakelock *wl)
|
||||
{
|
||||
list_move(&wl->lru, &wakelocks_lru_list);
|
||||
}
|
||||
|
||||
static void wakelocks_gc(void)
|
||||
{
|
||||
struct wakelock *wl, *aux;
|
||||
ktime_t now;
|
||||
|
||||
if (++wakelocks_gc_count <= WL_GC_COUNT_MAX)
|
||||
return;
|
||||
|
||||
now = ktime_get();
|
||||
list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {
|
||||
u64 idle_time_ns;
|
||||
bool active;
|
||||
|
||||
spin_lock_irq(&wl->ws.lock);
|
||||
idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
|
||||
active = wl->ws.active;
|
||||
spin_unlock_irq(&wl->ws.lock);
|
||||
|
||||
if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
|
||||
break;
|
||||
|
||||
if (!active) {
|
||||
wakeup_source_remove(&wl->ws);
|
||||
rb_erase(&wl->node, &wakelocks_tree);
|
||||
list_del(&wl->lru);
|
||||
kfree(wl->name);
|
||||
kfree(wl);
|
||||
decrement_wakelocks_number();
|
||||
}
|
||||
}
|
||||
wakelocks_gc_count = 0;
|
||||
}
|
||||
#else /* !CONFIG_PM_WAKELOCKS_GC */
|
||||
static inline void wakelocks_lru_add(struct wakelock *wl) {}
|
||||
static inline void wakelocks_lru_most_recent(struct wakelock *wl) {}
|
||||
static inline void wakelocks_gc(void) {}
|
||||
#endif /* !CONFIG_PM_WAKELOCKS_GC */
|
||||
|
||||
static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
|
||||
bool add_if_not_found)
|
||||
{
|
||||
struct rb_node **node = &wakelocks_tree.rb_node;
|
||||
struct rb_node *parent = *node;
|
||||
struct wakelock *wl;
|
||||
|
||||
while (*node) {
|
||||
int diff;
|
||||
|
||||
parent = *node;
|
||||
wl = rb_entry(*node, struct wakelock, node);
|
||||
diff = strncmp(name, wl->name, len);
|
||||
if (diff == 0) {
|
||||
if (wl->name[len])
|
||||
diff = -1;
|
||||
else
|
||||
return wl;
|
||||
}
|
||||
if (diff < 0)
|
||||
node = &(*node)->rb_left;
|
||||
else
|
||||
node = &(*node)->rb_right;
|
||||
}
|
||||
if (!add_if_not_found)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (wakelocks_limit_exceeded())
|
||||
return ERR_PTR(-ENOSPC);
|
||||
|
||||
/* Not found, we have to add a new one. */
|
||||
wl = kzalloc(sizeof(*wl), GFP_KERNEL);
|
||||
if (!wl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
wl->name = kstrndup(name, len, GFP_KERNEL);
|
||||
if (!wl->name) {
|
||||
kfree(wl);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
wl->ws.name = wl->name;
|
||||
wakeup_source_add(&wl->ws);
|
||||
rb_link_node(&wl->node, parent, node);
|
||||
rb_insert_color(&wl->node, &wakelocks_tree);
|
||||
wakelocks_lru_add(wl);
|
||||
increment_wakelocks_number();
|
||||
return wl;
|
||||
}
|
||||
|
||||
int pm_wake_lock(const char *buf)
|
||||
{
|
||||
const char *str = buf;
|
||||
struct wakelock *wl;
|
||||
u64 timeout_ns = 0;
|
||||
size_t len;
|
||||
int ret = 0;
|
||||
|
||||
while (*str && !isspace(*str))
|
||||
str++;
|
||||
|
||||
len = str - buf;
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
if (*str && *str != '\n') {
|
||||
/* Find out if there's a valid timeout string appended. */
|
||||
ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&wakelocks_lock);
|
||||
|
||||
wl = wakelock_lookup_add(buf, len, true);
|
||||
if (IS_ERR(wl)) {
|
||||
ret = PTR_ERR(wl);
|
||||
goto out;
|
||||
}
|
||||
if (timeout_ns) {
|
||||
u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
|
||||
|
||||
do_div(timeout_ms, NSEC_PER_MSEC);
|
||||
__pm_wakeup_event(&wl->ws, timeout_ms);
|
||||
} else {
|
||||
__pm_stay_awake(&wl->ws);
|
||||
}
|
||||
|
||||
wakelocks_lru_most_recent(wl);
|
||||
|
||||
out:
|
||||
mutex_unlock(&wakelocks_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pm_wake_unlock(const char *buf)
|
||||
{
|
||||
struct wakelock *wl;
|
||||
size_t len;
|
||||
int ret = 0;
|
||||
|
||||
len = strlen(buf);
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
if (buf[len-1] == '\n')
|
||||
len--;
|
||||
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&wakelocks_lock);
|
||||
|
||||
wl = wakelock_lookup_add(buf, len, false);
|
||||
if (IS_ERR(wl)) {
|
||||
ret = PTR_ERR(wl);
|
||||
goto out;
|
||||
}
|
||||
__pm_relax(&wl->ws);
|
||||
|
||||
wakelocks_lru_most_recent(wl);
|
||||
wakelocks_gc();
|
||||
|
||||
out:
|
||||
mutex_unlock(&wakelocks_lock);
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user