aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/power')
-rw-r--r--kernel/power/Kconfig17
-rw-r--r--kernel/power/earlysuspend.c21
-rw-r--r--kernel/power/hibernate.c66
-rw-r--r--kernel/power/main.c24
-rw-r--r--kernel/power/power.h1
-rw-r--r--kernel/power/swap.c29
-rw-r--r--kernel/power/wakelock.c29
7 files changed, 179 insertions, 8 deletions
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 56afb61..9fda331 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -26,6 +26,15 @@ config PM_WATCHDOG_TIMEOUT
Enable PM watchdog timer to catch lockup during early_suspend,
late_resume and suspend_finish.
+config FAST_BOOT
+ bool "Force suspend and show fake turn off which is same with suspend"
+ depends on SUSPEND
+ default n
+ ---help---
+ This allows you go to suspend instead to turn off. If this is
+ done, it goes to wake up instead to turn on. This works with power
+ source.
+
config HAS_WAKELOCK
bool
@@ -152,6 +161,14 @@ config FULL_PAGE_RECLAIM
dramatically decreased and small size of hibernation snapshot image
has benefit for fast booting.
+config FAST_RESUME
+ bool "Using fast resume during Suspend-to-Disk"
+ depends on HIBERNATION
+ ---help---
+ software_resume() function which triggers hibernation restore is
+ called faster on booting time by introducing new initcalls. This has
+ benefit for fast booting on hibernation
+
config PM_STD_PARTITION
string "Default resume partition"
depends on HIBERNATION
diff --git a/kernel/power/earlysuspend.c b/kernel/power/earlysuspend.c
index 1c293ce..e6303fd 100644
--- a/kernel/power/earlysuspend.c
+++ b/kernel/power/earlysuspend.c
@@ -20,6 +20,9 @@
#include <linux/syscalls.h> /* sys_sync */
#include <linux/wakelock.h>
#include <linux/workqueue.h>
+#ifdef CONFIG_ZRAM_FOR_ANDROID
+#include <asm/atomic.h>
+#endif /* CONFIG_ZRAM_FOR_ANDROID */
#include "power.h"
@@ -29,6 +32,11 @@ enum {
DEBUG_VERBOSE = 1U << 3,
};
static int debug_mask = DEBUG_USER_STATE;
+#ifdef CONFIG_ZRAM_FOR_ANDROID
+atomic_t optimize_comp_on = ATOMIC_INIT(0);
+EXPORT_SYMBOL(optimize_comp_on);
+#endif /* CONFIG_ZRAM_FOR_ANDROID */
+
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
static DEFINE_MUTEX(early_suspend_lock);
@@ -94,6 +102,9 @@ static void early_suspend(struct work_struct *work)
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock, irqflags);
+#ifdef CONFIG_ZRAM_FOR_ANDROID
+ atomic_set(&optimize_comp_on, 1);
+#endif /* CONFIG_ZRAM_FOR_ANDROID */
if (state == SUSPEND_REQUESTED)
state |= SUSPENDED;
else
@@ -145,6 +156,9 @@ static void late_resume(struct work_struct *work)
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock, irqflags);
+#ifdef CONFIG_ZRAM_FOR_ANDROID
+ atomic_set(&optimize_comp_on, 0);
+#endif /* CONFIG_ZRAM_FOR_ANDROID */
if (state == SUSPENDED)
state &= ~SUSPENDED;
else
@@ -174,6 +188,9 @@ abort:
pm_wd_del_timer(&timer);
}
+#ifdef CONFIG_FAST_BOOT
+extern bool fake_shut_down;
+#endif
void request_suspend_state(suspend_state_t new_state)
{
unsigned long irqflags;
@@ -198,6 +215,10 @@ void request_suspend_state(suspend_state_t new_state)
state |= SUSPEND_REQUESTED;
queue_work(suspend_work_queue, &early_suspend_work);
} else if (old_sleep && new_state == PM_SUSPEND_ON) {
+#ifdef CONFIG_FAST_BOOT
+ if (fake_shut_down)
+ fake_shut_down = false;
+#endif
state &= ~SUSPEND_REQUESTED;
wake_lock(&main_wake_lock);
queue_work(suspend_work_queue, &late_resume_work);
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 8884c27..c260280 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -14,6 +14,7 @@
#include <linux/reboot.h>
#include <linux/string.h>
#include <linux/device.h>
+#include <linux/async.h>
#include <linux/kmod.h>
#include <linux/delay.h>
#include <linux/fs.h>
@@ -24,13 +25,16 @@
#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"
static int nocompress = 0;
-static int noresume = 0;
+int noresume;
+static int resume_wait = 0;
static char resume_file[256] = CONFIG_PM_STD_PARTITION;
dev_t swsusp_resume_device;
sector_t swsusp_resume_block;
@@ -727,12 +731,30 @@ 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
* to wait for this to finish.
*/
wait_for_device_probe();
+
+ if (resume_wait) {
+ while ((swsusp_resume_device = name_to_dev_t(resume_file)) == 0)
+ msleep(10);
+ async_synchronize_full();
+ }
+
/*
* We can't depend on SCSI devices being available after loading
* one of their modules until scsi_complete_async_scans() is
@@ -810,7 +832,11 @@ close_finish:
goto Finish;
}
+#ifdef CONFIG_FAST_RESUME
+resume_initcall(software_resume);
+#else
late_initcall(software_resume);
+#endif
static const char * const hibernation_modes[] = {
@@ -1002,11 +1028,42 @@ static ssize_t reserved_size_store(struct kobject *kobj,
power_attr(reserved_size);
+#ifdef CONFIG_FAST_RESUME
+static ssize_t noresume_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", noresume);
+}
+
+static ssize_t noresume_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ if (sscanf(buf, "%d", &noresume) == 1) {
+ noresume = !!noresume;
+ if (noresume) {
+ if (!swsusp_resume_device)
+ swsusp_resume_device =
+ name_to_dev_t(resume_file);
+ swsusp_check();
+ swsusp_close(FMODE_READ);
+ }
+ return n;
+ }
+
+ return -EINVAL;
+}
+
+power_attr(noresume);
+#endif
+
static struct attribute * g[] = {
&disk_attr.attr,
&resume_attr.attr,
&image_size_attr.attr,
&reserved_size_attr.attr,
+#ifdef CONFIG_FAST_RESUME
+ &noresume_attr.attr,
+#endif
NULL,
};
@@ -1061,7 +1118,14 @@ static int __init noresume_setup(char *str)
return 1;
}
+static int __init resumewait_setup(char *str)
+{
+ resume_wait = 1;
+ return 1;
+}
+
__setup("noresume", noresume_setup);
__setup("resume_offset=", resume_offset_setup);
__setup("resume=", resume_setup);
__setup("hibernate=", hibernate_setup);
+__setup("resumewait=", resumewait_setup);
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 7f6987f..9c54ff7 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -190,6 +190,12 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
#endif
return (s - buf);
}
+#ifdef CONFIG_FAST_BOOT
+bool fake_shut_down = false;
+EXPORT_SYMBOL(fake_shut_down);
+
+extern void wakelock_force_suspend(void);
+#endif
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
@@ -220,15 +226,29 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
break;
}
- if (state < PM_SUSPEND_MAX && *s)
+
+#ifdef CONFIG_FAST_BOOT
+ if (len == 4 && !strncmp(buf, "dmem", len)) {
+ pr_info("%s: fake shut down!!!\n", __func__);
+ fake_shut_down = true;
+ state = PM_SUSPEND_MEM;
+ }
+#endif
+
+ if (state < PM_SUSPEND_MAX && *s) {
#ifdef CONFIG_EARLYSUSPEND
if (state == PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state);
}
+#ifdef CONFIG_FAST_BOOT
+ if (fake_shut_down)
+ wakelock_force_suspend();
+#endif
#else
error = enter_state(state);
#endif
+ }
#endif
Exit:
@@ -683,7 +703,7 @@ static ssize_t mali_lock_store(struct kobject *kobj,
mali_lock_cnt = mali_dvfs_bottom_lock_pop();
if (mali_lock_cnt == 0)
mali_lock_val = 0;
- } else if (val > 0 && val < 4) { /* lock with level */
+ } else if (val > 0 && val < 5) { /* lock with level */
mali_lock_cnt = mali_dvfs_bottom_lock_push(val);
if (mali_lock_val < val)
mali_lock_val = val;
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 474d75b..fac1ce8 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -72,6 +72,7 @@ static struct kobj_attribute _name##_attr = { \
.store = _name##_store, \
}
+extern int noresume;
/* Preferred image size in bytes (default 500 MB) */
extern unsigned long image_size;
/* Size of memory reserved for drivers (default SPARE_PAGES x PAGE_SIZE) */
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index 7c97c3a..2a2dc30 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -372,6 +372,15 @@ static int swap_writer_finish(struct swap_map_handle *handle,
LZO_HEADER, PAGE_SIZE)
#define LZO_CMP_SIZE (LZO_CMP_PAGES * PAGE_SIZE)
+/*
+ * lzo experimental compression ratio.
+ * When compression is used for hibernation, swap size is not required for worst
+ * case. So we use an experimental compression ratio. If the swap size is not
+ * enough, then alloc_swapdev_block() return fails and hibernation codes handle
+ * the error well.
+ */
+#define LZO_RATIO(x) ((x) / 2)
+
/**
* save_image - save the suspend image data
*/
@@ -437,7 +446,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
struct bio *bio;
struct timeval start;
struct timeval stop;
- size_t off, unc_len, cmp_len;
+ size_t off, unc_len, cmp_len, total;
unsigned char *unc, *cmp, *wrk, *page;
page = (void *)__get_free_page(__GFP_WAIT | __GFP_HIGH);
@@ -477,6 +486,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
if (!m)
m = 1;
nr_pages = 0;
+ total = 0;
bio = NULL;
do_gettimeofday(&start);
for (;;) {
@@ -529,6 +539,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
if (ret)
goto out_finish;
}
+ total += DIV_ROUND_UP(LZO_HEADER + cmp_len, PAGE_SIZE);
}
out_finish:
@@ -541,6 +552,11 @@ out_finish:
else
printk(KERN_CONT "\n");
swsusp_show_speed(&start, &stop, nr_to_write, "Wrote");
+ pr_info("PM: %lu->%lu kbytes, %d%% compressed\n",
+ nr_to_write * PAGE_SIZE / 1024,
+ total * PAGE_SIZE / 1024,
+ 100 - ((total * 100) / nr_to_write));
+ image_size = total * PAGE_SIZE;
vfree(cmp);
vfree(unc);
@@ -564,8 +580,8 @@ static int enough_swap(unsigned int nr_pages, unsigned int flags)
pr_debug("PM: Free swap pages: %u\n", free_swap);
- required = PAGES_FOR_IO + ((flags & SF_NOCOMPRESS_MODE) ?
- nr_pages : (nr_pages * LZO_CMP_PAGES) / LZO_UNC_PAGES + 1);
+ required = PAGES_FOR_IO + ((flags & SF_NOCOMPRESS_MODE) ? nr_pages :
+ LZO_RATIO((nr_pages * LZO_CMP_PAGES) / LZO_UNC_PAGES + 1));
return free_swap > required;
}
@@ -943,8 +959,11 @@ int swsusp_check(void)
if (!memcmp(HIBERNATE_SIG, swsusp_header->sig, 10)) {
memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10);
/* Reset swap signature now */
- error = hib_bio_write_page(swsusp_resume_block,
- swsusp_header, NULL);
+#if defined(CONFIG_FAST_RESUME) && defined(CONFIG_SLP)
+ if (noresume)
+#endif
+ error = hib_bio_write_page(swsusp_resume_block,
+ swsusp_header, NULL);
} else {
error = -EINVAL;
}
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index b2e149a..104f6dc 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -22,6 +22,9 @@
#ifdef CONFIG_WAKELOCK_STAT
#include <linux/proc_fs.h>
#endif
+#ifdef CONFIG_FAST_BOOT
+#include <linux/delay.h>
+#endif
#include "power.h"
enum {
@@ -250,11 +253,18 @@ static long has_wake_lock_locked(int type)
}
return max_timeout;
}
+#ifdef CONFIG_FAST_BOOT
+extern bool fake_shut_down;
+#endif
long has_wake_lock(int type)
{
long ret;
unsigned long irqflags;
+#ifdef CONFIG_FAST_BOOT
+ if (fake_shut_down)
+ return 0;
+#endif
spin_lock_irqsave(&list_lock, irqflags);
ret = has_wake_lock_locked(type);
if (ret && (debug_mask & DEBUG_WAKEUP) && type == WAKE_LOCK_SUSPEND)
@@ -558,6 +568,25 @@ int wake_lock_active(struct wake_lock *lock)
}
EXPORT_SYMBOL(wake_lock_active);
+#ifdef CONFIG_FAST_BOOT
+void wakelock_force_suspend(void)
+{
+ static int cnt;
+
+ if (cnt > 0) {
+ pr_info("%s: duplicated\n", __func__);
+ return;
+ }
+ cnt++;
+
+ msleep(3000);
+ pr_info("%s: fake shut down\n", __func__);
+ queue_work(suspend_work_queue, &suspend_work);
+
+ cnt = 0;
+}
+#endif
+
static int wakelock_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, wakelock_stats_show, NULL);